Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 105 additions & 5 deletions simpleAPI/simpleAPI_mqtt.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ def _on_connect(self, client, userdata, flags, rc):
"""Callback for successful MQTT connection."""
if rc == 0:
log.info(f"Connected to MQTT broker at {self.host}:{self.port}")

# Publish API revision
self._publish_revision()

# Subscribe to specific openWB component topics
client.subscribe("openWB/bat/#", qos=0)
client.subscribe("openWB/pv/#", qos=0)
Expand Down Expand Up @@ -271,7 +275,11 @@ def _transform_chargepoint_topic(self, simple_base: str) -> Optional[str]:
log.debug(f"DEBUG: Found vehicle name pattern, transforming: {simple_base}")
simple_base = re.sub(r'/get/connected_vehicle/info/name$', '/vehicle_name', simple_base)
simple_base = re.sub(r'/connected_vehicle/info/name$', '/vehicle_name', simple_base)

elif '/connected_vehicle/soc' in simple_base:
log.debug(f"DEBUG: Found connected_vehicle soc pattern, transforming: {simple_base}")
simple_base = re.sub(r'/get/connected_vehicle/soc$', '/soc', simple_base)
simple_base = re.sub(r'/connected_vehicle/soc$', '/soc', simple_base)

# Keep only config topics that are in the allowed list
if '/config/' in simple_base and not re.search(r'/(chargemode|vehicle_name)$', simple_base):
allowed_config_paths = [
Expand Down Expand Up @@ -304,11 +312,17 @@ def _transform_chargepoint_topic(self, simple_base: str) -> Optional[str]:
# Handle get topics - remove /get/ prefix
if '/get/' in simple_base:
simple_base = simple_base.replace('/get/', '/')


# Special handling for soc-related topics
if re.search(r'/soc$', simple_base):
simple_base = re.sub(r'/soc$', '/pro_soc', simple_base)
elif re.search(r'/soc_timestamp$', simple_base):
simple_base = re.sub(r'/soc_timestamp$', '/pro_soc_timestamp', simple_base)

# Filter out unwanted topics - but exclude already transformed ones
if not re.search(r'/(chargemode|vehicle_name)$', simple_base):
if not re.search(r'/(chargemode|vehicle_name|soc|pro_soc|pro_soc_timestamp)$', simple_base):
unwanted_patterns = [
r'/connected_vehicle/(info|config|soc)/',
r'/connected_vehicle/(info|config)/',
r'/max_evse_current$',
r'/current_branch$',
r'/current_commit$'
Expand All @@ -320,7 +334,7 @@ def _transform_chargepoint_topic(self, simple_base: str) -> Optional[str]:
else:
# For non-get topics, also filter out remaining connected_vehicle topics
# but exclude the ones we already transformed
if not re.search(r'/(chargemode|vehicle_name)$', simple_base):
if not re.search(r'/(chargemode|vehicle_name|soc)$', simple_base):
if re.search(r'/connected_vehicle/', simple_base):
log.debug(f"DEBUG: Filtering out connected_vehicle topic: {simple_base}")
return None
Expand Down Expand Up @@ -424,12 +438,88 @@ def _cache_charge_template(self, topic: str, payload: str):
charge_template = json.loads(payload)
self.charge_template_cache[chargepoint_id] = charge_template
log.debug(f"Cached charge_template for chargepoint {chargepoint_id}")

# Publish read topics for charge_template values
self._publish_charge_template_read_topics(chargepoint_id, charge_template)

except json.JSONDecodeError as e:
log.warning(f"Failed to parse charge_template JSON for {topic}: {e}")
except Exception as e:
log.error(f"Error caching charge_template for {topic}: {e}")

def _publish_charge_template_read_topics(self, chargepoint_id: str, charge_template: Dict[str, Any]):
"""Publish readable topics for charge_template values."""
try:
chargemode = charge_template.get('chargemode', {})

# Extract min_current from pv_charging
pv_charging = chargemode.get('pv_charging', {})
if 'min_current' in pv_charging:
topic = f"openWB/simpleAPI/chargepoint/{chargepoint_id}/minimal_permanent_current"
self._publish_if_changed(topic, pv_charging['min_current'])

# Also publish for lowest ID if this is it
if 'chargepoint' in self.lowest_ids and self.lowest_ids['chargepoint'] == int(chargepoint_id):
simple_topic = "openWB/simpleAPI/chargepoint/minimal_permanent_current"
self._publish_if_changed(simple_topic, pv_charging['min_current'])

# Extract min_soc from pv_charging (minimal_pv_soc)
if 'min_soc' in pv_charging:
topic = f"openWB/simpleAPI/chargepoint/{chargepoint_id}/minimal_pv_soc"
self._publish_if_changed(topic, pv_charging['min_soc'])

# Also publish for lowest ID if this is it
if 'chargepoint' in self.lowest_ids and self.lowest_ids['chargepoint'] == int(chargepoint_id):
simple_topic = "openWB/simpleAPI/chargepoint/minimal_pv_soc"
self._publish_if_changed(simple_topic, pv_charging['min_soc'])

# Extract max_price from eco_charging
eco_charging = chargemode.get('eco_charging', {})
if 'max_price' in eco_charging:
topic = f"openWB/simpleAPI/chargepoint/{chargepoint_id}/max_price_eco"
self._publish_if_changed(topic, eco_charging['max_price'])

# Also publish for lowest ID if this is it
if 'chargepoint' in self.lowest_ids and self.lowest_ids['chargepoint'] == int(chargepoint_id):
simple_topic = "openWB/simpleAPI/chargepoint/max_price_eco"
self._publish_if_changed(simple_topic, eco_charging['max_price'])

# Extract instant_charging_limit values
instant_charging = chargemode.get('instant_charging', {})
limit = instant_charging.get('limit', {})

if 'selected' in limit:
topic = f"openWB/simpleAPI/chargepoint/{chargepoint_id}/instant_charging_limit"
self._publish_if_changed(topic, limit['selected'])

# Also publish for lowest ID if this is it
if 'chargepoint' in self.lowest_ids and self.lowest_ids['chargepoint'] == int(chargepoint_id):
simple_topic = "openWB/simpleAPI/chargepoint/instant_charging_limit"
self._publish_if_changed(simple_topic, limit['selected'])

if 'amount' in limit:
# Convert from internal value (Wh) to kWh for display
amount_kwh = limit['amount'] / 1000
topic = f"openWB/simpleAPI/chargepoint/{chargepoint_id}/instant_charging_limit_amount"
self._publish_if_changed(topic, amount_kwh)

# Also publish for lowest ID if this is it
if 'chargepoint' in self.lowest_ids and self.lowest_ids['chargepoint'] == int(chargepoint_id):
simple_topic = "openWB/simpleAPI/chargepoint/instant_charging_limit_amount"
self._publish_if_changed(simple_topic, amount_kwh)

if 'soc' in limit:
topic = f"openWB/simpleAPI/chargepoint/{chargepoint_id}/instant_charging_limit_soc"
self._publish_if_changed(topic, limit['soc'])

# Also publish for lowest ID if this is it
if 'chargepoint' in self.lowest_ids and self.lowest_ids['chargepoint'] == int(chargepoint_id):
simple_topic = "openWB/simpleAPI/chargepoint/instant_charging_limit_soc"
self._publish_if_changed(simple_topic, limit['soc'])

except Exception as e:
log.error(f"Error publishing charge_template read topics: {e}")

def _handle_write_operation(self, topic: str, payload: str):
"""Handle write operations from simpleAPI set topics."""
try:
Expand Down Expand Up @@ -777,6 +867,16 @@ def _publish_json(self, topic: str, data: Dict[str, Any]):
except Exception as e:
log.error(f"Failed to publish JSON to {topic}: {e}")

def _publish_revision(self):
"""Publish API revision number."""
try:
revision_topic = "openWB/simpleAPI/revision"
revision_value = "2"
self.client.publish(revision_topic, revision_value, qos=0, retain=True)
log.info(f"Published API revision: {revision_value}")
except Exception as e:
log.error(f"Failed to publish revision: {e}")

def connect(self):
"""Establish connection to MQTT broker."""
try:
Expand Down