diff --git a/srnemqtt/consumers/mqtt.py b/srnemqtt/consumers/mqtt.py index 51d26b1..6600db3 100644 --- a/srnemqtt/consumers/mqtt.py +++ b/srnemqtt/consumers/mqtt.py @@ -116,28 +116,37 @@ PayloadType: TypeAlias = str | bytes | bytearray | int | float | None class MqttConsumer(BaseConsumer): - client: mqtt.Client initialized: List[str] + _client: mqtt.Client | None = None + def __init__(self, settings: Dict[str, Any]) -> None: self.initialized = [] super().__init__(settings) - self.client = mqtt.Client(client_id=settings["client"]["id"], userdata=self) - self.client.on_connect = self.on_connect - self.client.on_message = self.on_message - self.client.on_disconnect = self.on_disconnect - self.client.on_connect_fail = self.on_connect_fail + + @property + def client(self) -> mqtt.Client: + if self._client is not None: + return self._client + + self._client = mqtt.Client( + client_id=self.settings["client"]["id"], userdata=self + ) + self._client.on_connect = self.on_connect + self._client.on_message = self.on_message + self._client.on_disconnect = self.on_disconnect + self._client.on_connect_fail = self.on_connect_fail # Will must be set before connecting!! - self.client.will_set( + self._client.will_set( f"{self.topic_prefix}/available", payload="offline", retain=True ) while True: try: - self.client.connect( - settings["client"]["host"], - settings["client"]["port"], - settings["client"]["keepalive"], + self._client.connect( + self.settings["client"]["host"], + self.settings["client"]["port"], + self.settings["client"]["keepalive"], ) break except OSError as err: @@ -151,6 +160,7 @@ class MqttConsumer(BaseConsumer): raise print(err) sleep(0.1) + return self._client def config(self, settings: Dict[str, Any]): super().config(settings) @@ -167,9 +177,19 @@ class MqttConsumer(BaseConsumer): settings.setdefault("discovery_prefix", "homeassistant") + _controller_id: str | None = None + + @property + def controller_id(self) -> str: + assert self.controller is not None + # Controller serial is fetched from device, cache it. + if self._controller_id is None: + self._controller_id = self.controller.serial + return f"{self.controller.manufacturer_id}_{self._controller_id}" + @property def topic_prefix(self): - return f"{self.settings['prefix']}/{self.settings['device_id']}" + return f"{self.settings['prefix']}/{self.controller_id}" def get_ha_config( self, @@ -181,21 +201,25 @@ class MqttConsumer(BaseConsumer): state_class: Optional[str] = None, ): assert state_class in [None, "measurement", "total", "total_increasing"] + assert self.controller is not None res = { "~": f"{self.topic_prefix}", - "unique_id": f"{self.settings['device_id']}_{id}", + "unique_id": f"{self.controller_id}_{id}", + "object_id": f"{self.controller_id}_{id}", "availability_topic": "~/available", "state_topic": f"~/{id}", "name": name, "device": { "identifiers": [ - self.settings["device_id"], + self.controller_id, ], - # TODO: Get charger serial and use for identifier instead - # See: https://www.home-assistant.io/integrations/sensor.mqtt/#device - # "via_device": self.settings["device_id"], + "manufacturer": self.controller.manufacturer, + "model": self.controller.model, + "hw_version": self.controller.version, + "via_device": self.settings["device_id"], "suggested_area": "Solar panel", + "name": self.controller.name, }, "force_update": True, "expire_after": expiry, @@ -253,22 +277,25 @@ class MqttConsumer(BaseConsumer): def write(self, data: Dict[str, PayloadType]): self.client.publish(f"{self.topic_prefix}/raw", payload=json.dumps(data)) - for k, v in data.items(): - if k in MAP_VALUES: - if k not in self.initialized: - km = MAP_VALUES[DataName(k)] - pretty_name = k.replace("_", " ").capitalize() + for dataname, data_value in data.items(): + if dataname in MAP_VALUES: + if dataname not in self.initialized: + km = MAP_VALUES[DataName(dataname)] + pretty_name = dataname.replace("_", " ").capitalize() disc_prefix = self.settings["discovery_prefix"] - device_id = self.settings["device_id"] self.client.publish( - f"{disc_prefix}/sensor/{device_id}_{k}/config", - payload=json.dumps(self.get_ha_config(k, pretty_name, **km)), + f"{disc_prefix}/sensor/{self.controller_id}/{dataname}/config", + payload=json.dumps( + self.get_ha_config(dataname, pretty_name, **km) + ), retain=True, ) - self.initialized.append(k) + self.initialized.append(dataname) - self.client.publish(f"{self.topic_prefix}/{k}", v, retain=True) + self.client.publish( + f"{self.topic_prefix}/{dataname}", data_value, retain=True + ) def exit(self): self.client.publish(