From e5a5d27afc7bc6176935e6201e9ce6e74541d0d7 Mon Sep 17 00:00:00 2001 From: andrea Date: Sat, 27 Apr 2024 11:59:42 +0200 Subject: [PATCH 01/22] Added openenig and closing time attributes --- custom_components/myhome/const.py | 2 ++ custom_components/myhome/cover.py | 40 ++++++++++++++++++++++++++-- custom_components/myhome/validate.py | 4 +++ 3 files changed, 44 insertions(+), 2 deletions(-) mode change 100644 => 100755 custom_components/myhome/const.py mode change 100644 => 100755 custom_components/myhome/cover.py mode change 100644 => 100755 custom_components/myhome/validate.py diff --git a/custom_components/myhome/const.py b/custom_components/myhome/const.py old mode 100644 new mode 100755 index 9c560b6..358dc6d --- a/custom_components/myhome/const.py +++ b/custom_components/myhome/const.py @@ -46,3 +46,5 @@ CONF_SHORT_RELEASE = "pushbutton_short_release" CONF_LONG_PRESS = "pushbutton_long_press" CONF_LONG_RELEASE = "pushbutton_long_release" +CONF_SHUTTER_OPENING_TIME = "opening_time" +CONF_SHUTTER_CLOSING_TIME = "closing_time" \ No newline at end of file diff --git a/custom_components/myhome/cover.py b/custom_components/myhome/cover.py old mode 100644 new mode 100755 index 13c42e0..773b9a7 --- a/custom_components/myhome/cover.py +++ b/custom_components/myhome/cover.py @@ -29,9 +29,12 @@ CONF_ADVANCED_SHUTTER, DOMAIN, LOGGER, + CONF_SHUTTER_OPENING_TIME, + CONF_SHUTTER_CLOSING_TIME, ) from .myhome_device import MyHOMEEntity from .gateway import MyHOMEGatewayHandler +from datetime import datetime async def async_setup_entry(hass, config_entry, async_add_entities): @@ -54,6 +57,8 @@ async def async_setup_entry(hass, config_entry, async_add_entities): manufacturer=_configured_covers[_cover][CONF_MANUFACTURER], model=_configured_covers[_cover][CONF_DEVICE_MODEL], gateway=hass.data[DOMAIN][config_entry.data[CONF_MAC]][CONF_ENTITY], + opening_time=_configured_covers[_cover][CONF_SHUTTER_OPENING_TIME], + closing_time=_configured_covers[_cover][CONF_SHUTTER_CLOSING_TIME], ) _covers.append(_cover) @@ -86,6 +91,8 @@ def __init__( manufacturer: str, model: str, gateway: MyHOMEGatewayHandler, + opening_time: int, + closing_time: int ): super().__init__( hass=hass, @@ -107,6 +114,8 @@ def __init__( self._attr_supported_features = CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE | CoverEntityFeature.STOP if advanced: self._attr_supported_features |= CoverEntityFeature.SET_POSITION + elif opening_time > 0 and closing_time > 0: + self._attr_supported_features |= CoverEntityFeature.SET_POSITION self._gateway_handler = gateway self._attr_extra_state_attributes = { @@ -120,6 +129,9 @@ def __init__( self._attr_is_opening = None self._attr_is_closing = None self._attr_is_closed = None + self._attr_last_event = datetime.now() + self._attr_opening_time = opening_time + self._attr_closing_time = closing_time async def async_update(self): """Update the entity. @@ -153,11 +165,35 @@ def handle_event(self, message: OWNAutomationEvent): self._gateway_handler.log_id, message.human_readable_log, ) + + if message.current_position is not None: + self._attr_current_cover_position = message.current_position + elif self._attr_last_event is not None and self._attr_opening_time > 0 and self._attr_closing_time > 0: + elapsed_seconds = (datetime.now() - self._attr_last_event).total_seconds() + if elapsed_seconds > 0: + if self._attr_is_opening: + if self._attr_opening_time < elapsed_seconds: + self._attr_current_cover_position = 100 + elif (self._attr_current_cover_position is not None): + self._attr_current_cover_position = round(min(100, self._attr_current_cover_position + (100 * elapsed_seconds / self._attr_opening_time)), 0) + elif self._attr_is_closing: + if self._attr_closing_time < elapsed_seconds: + self._attr_current_cover_position = 0 + elif self._attr_current_cover_position is not None: + self._attr_current_cover_position = round(max(0, self._attr_current_cover_position - (100 * elapsed_seconds / self._attr_closing_time)), 0) + + LOGGER.info( + "%s %s", + self._gateway_handler.log_id, + self._attr_current_cover_position, + ) + + self._attr_last_event = datetime.now() self._attr_is_opening = message.is_opening self._attr_is_closing = message.is_closing if message.is_closed is not None: self._attr_is_closed = message.is_closed - if message.current_position is not None: - self._attr_current_cover_position = message.current_position + elif self._attr_current_cover_position is not None: + self._attr_is_closed = self._attr_current_cover_position == 0 self.async_schedule_update_ha_state() diff --git a/custom_components/myhome/validate.py b/custom_components/myhome/validate.py old mode 100644 new mode 100755 index a27b09c..dcef874 --- a/custom_components/myhome/validate.py +++ b/custom_components/myhome/validate.py @@ -52,6 +52,8 @@ CONF_COOLING_SUPPORT, CONF_STANDALONE, CONF_CENTRAL, + CONF_SHUTTER_OPENING_TIME, + CONF_SHUTTER_CLOSING_TIME, ) @@ -336,6 +338,8 @@ def __call__(self, data): Required(CONF_NAME): str, Optional(CONF_ENTITY_NAME): str, Optional(CONF_ADVANCED_SHUTTER, default=False): Boolean(), + Optional(CONF_SHUTTER_OPENING_TIME, default=0): int, + Optional(CONF_SHUTTER_CLOSING_TIME, default=0): int, Optional(CONF_MANUFACTURER, default="BTicino S.p.A."): str, Optional(CONF_DEVICE_MODEL): Coerce(str), } From bba3c6420351c52d3139b1606f3a7157183d407e Mon Sep 17 00:00:00 2001 From: andrea Date: Sat, 4 May 2024 10:42:47 +0200 Subject: [PATCH 02/22] Add the support to move a cover to a position without advanced device --- custom_components/myhome/cover.py | 44 ++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/custom_components/myhome/cover.py b/custom_components/myhome/cover.py index 773b9a7..7253241 100755 --- a/custom_components/myhome/cover.py +++ b/custom_components/myhome/cover.py @@ -35,7 +35,7 @@ from .myhome_device import MyHOMEEntity from .gateway import MyHOMEGatewayHandler from datetime import datetime - +import asyncio async def async_setup_entry(hass, config_entry, async_add_entities): if PLATFORM not in hass.data[DOMAIN][config_entry.data[CONF_MAC]][CONF_PLATFORMS]: @@ -110,6 +110,9 @@ def __init__( self._interface = interface self._full_where = f"{self._where}#4#{self._interface}" if self._interface is not None else self._where + self._attr_opening_time = opening_time + self._attr_closing_time = closing_time + self._attr_advanced = advanced self._attr_supported_features = CoverEntityFeature.OPEN | CoverEntityFeature.CLOSE | CoverEntityFeature.STOP if advanced: @@ -130,8 +133,6 @@ def __init__( self._attr_is_closing = None self._attr_is_closed = None self._attr_last_event = datetime.now() - self._attr_opening_time = opening_time - self._attr_closing_time = closing_time async def async_update(self): """Update the entity. @@ -152,7 +153,38 @@ async def async_set_cover_position(self, **kwargs): """Move the cover to a specific position.""" if ATTR_POSITION in kwargs: position = kwargs[ATTR_POSITION] - await self._gateway_handler.send(OWNAutomationCommand.set_shutter_level(self._full_where, position)) + if self._attr_advanced: + await self._gateway_handler.send(OWNAutomationCommand.set_shutter_level(self._full_where, position)) + elif self._attr_opening_time > 0 or self._attr_closing_time > 0: + + if self._attr_is_closing or self._attr_is_closing: + await self._gateway_handler.send(OWNAutomationCommand.stop_shutter(self._full_where)) + + if self._attr_current_cover_position is None: + return + + required_move = int(position - self._attr_current_cover_position) + if required_move > 0: + """ open """ + required_time = abs(self._attr_opening_time * required_move / 100) + LOGGER.debug( + "Open -> Required time %s", + required_time, + ) + await self._gateway_handler.send(OWNAutomationCommand.raise_shutter(self._full_where)) + await asyncio.sleep(required_time) + await self._gateway_handler.send(OWNAutomationCommand.stop_shutter(self._full_where)) + elif required_move < 0: + """ close """ + required_time = abs(self._attr_closing_time * required_move / 100) + LOGGER.debug( + "Close -> Required time %s", + required_time, + ) + await self._gateway_handler.send(OWNAutomationCommand.lower_shutter(self._full_where)) + await asyncio.sleep(required_time) + await self._gateway_handler.send(OWNAutomationCommand.stop_shutter(self._full_where)) + async def async_stop_cover(self, **kwargs): # pylint: disable=unused-argument """Stop the cover.""" @@ -182,7 +214,7 @@ def handle_event(self, message: OWNAutomationEvent): elif self._attr_current_cover_position is not None: self._attr_current_cover_position = round(max(0, self._attr_current_cover_position - (100 * elapsed_seconds / self._attr_closing_time)), 0) - LOGGER.info( + LOGGER.debug( "%s %s", self._gateway_handler.log_id, self._attr_current_cover_position, @@ -196,4 +228,4 @@ def handle_event(self, message: OWNAutomationEvent): elif self._attr_current_cover_position is not None: self._attr_is_closed = self._attr_current_cover_position == 0 - self.async_schedule_update_ha_state() + self.async_schedule_update_ha_state() \ No newline at end of file From 4cadffe4f3d6e63751966f2c93f8ac19fb1e9d16 Mon Sep 17 00:00:00 2001 From: andrea Date: Sat, 4 May 2024 11:27:53 +0200 Subject: [PATCH 03/22] Changed version number --- custom_components/myhome/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/myhome/manifest.json b/custom_components/myhome/manifest.json index b32a3e7..d9176fc 100644 --- a/custom_components/myhome/manifest.json +++ b/custom_components/myhome/manifest.json @@ -2,7 +2,7 @@ "domain": "myhome", "integration_type": "hub", "name": "MyHOME", - "version": "0.9.3", + "version": "0.9.4", "config_flow": true, "documentation": "https://github.com/anotherjulien/MyHOME", "issue_tracker": "https://github.com/anotherjulien/MyHOME/issues", From 3b03182396c57b6215a04f16e2d09af11f391a5c Mon Sep 17 00:00:00 2001 From: Jacopo Jannone Date: Mon, 13 May 2024 21:11:59 +0200 Subject: [PATCH 04/22] Fix string format bug in logging --- custom_components/myhome/gateway.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/myhome/gateway.py b/custom_components/myhome/gateway.py index 821a20d..74ccf49 100644 --- a/custom_components/myhome/gateway.py +++ b/custom_components/myhome/gateway.py @@ -378,7 +378,7 @@ async def sending_loop(self, worker_id: int): while not self._terminate_sender: task = await self.send_buffer.get() LOGGER.debug( - "%s Message `%s` was successfully unqueued by worker %s.", + "%s (%s) Message `%s` was successfully unqueued by worker %s.", self.name, self.gateway.host, task["message"], From c1e78845c3391d59b35e1b9d529447d6e2c72717 Mon Sep 17 00:00:00 2001 From: Jacopo Jannone Date: Mon, 13 May 2024 21:12:21 +0200 Subject: [PATCH 05/22] Implement raw actuators as binary sensors --- custom_components/myhome/binary_sensor.py | 105 ++++++++++++++++++++++ custom_components/myhome/validate.py | 2 + 2 files changed, 107 insertions(+) diff --git a/custom_components/myhome/binary_sensor.py b/custom_components/myhome/binary_sensor.py index 31f5aaa..363208e 100644 --- a/custom_components/myhome/binary_sensor.py +++ b/custom_components/myhome/binary_sensor.py @@ -33,6 +33,9 @@ CONF_DEVICE_MODEL, CONF_DEVICE_CLASS, CONF_INVERTED, + CONF_BUS_INTERFACE, + CONF_ICON, + CONF_ICON_ON, DOMAIN, LOGGER, ) @@ -98,6 +101,24 @@ async def async_setup_entry(hass, config_entry, async_add_entities): gateway=hass.data[DOMAIN][config_entry.data[CONF_MAC]][CONF_ENTITY], ) _binary_sensors.append(_binary_sensor) + elif _who == 1 and _device_class == BinarySensorDeviceClass.POWER: + _binary_sensor = MyHOMEActuator( + hass=hass, + device_id=_binary_sensor, + who=_configured_binary_sensors[_binary_sensor][CONF_WHO], + where=_configured_binary_sensors[_binary_sensor][CONF_WHERE], + icon=_configured_binary_sensors[_binary_sensor][CONF_ICON], + icon_on=_configured_binary_sensors[_binary_sensor][CONF_ICON_ON], + name=_configured_binary_sensors[_binary_sensor][CONF_NAME], + entity_name=_configured_binary_sensors[_binary_sensor][CONF_ENTITY_NAME], + inverted=_configured_binary_sensors[_binary_sensor][CONF_INVERTED], + interface=_configured_binary_sensors[_binary_sensor][CONF_BUS_INTERFACE] if CONF_BUS_INTERFACE in _configured_binary_sensors[_binary_sensor] else None, + device_class=_device_class, + manufacturer=_configured_binary_sensors[_binary_sensor][CONF_MANUFACTURER], + model=_configured_binary_sensors[_binary_sensor][CONF_DEVICE_MODEL], + gateway=hass.data[DOMAIN][config_entry.data[CONF_MAC]][CONF_ENTITY], + ) + _binary_sensors.append(_binary_sensor) async_add_entities(_binary_sensors) @@ -334,3 +355,87 @@ def handle_event(self, message: OWNLightingEvent): self._attr_force_update = True self.async_write_ha_state() self._attr_force_update = False + + +class MyHOMEActuator(MyHOMEEntity, BinarySensorEntity): + def __init__( + self, + hass, + name: str, + entity_name: str, + icon: str, + icon_on: str, + device_id: str, + who: str, + where: str, + inverted: bool, + interface: str, + device_class: str, + manufacturer: str, + model: str, + gateway: MyHOMEGatewayHandler, + ): + super().__init__( + hass=hass, + name=name, + platform=PLATFORM, + device_id=device_id, + who=who, + where=where, + manufacturer=manufacturer, + model=model, + gateway=gateway, + ) + + self._inverted = inverted + + self._attr_device_class = device_class + self._attr_name = entity_name if entity_name else self._attr_device_class.replace("_", " ").capitalize() + + self._attr_unique_id = f"{gateway.mac}-{self._device_id}-{self._attr_device_class}" + + self._interface = interface + self._full_where = f"{self._where}#4#{self._interface}" if self._interface is not None else self._where + + self._attr_extra_state_attributes = { + "A": where[: len(where) // 2], + "PL": where[len(where) // 2 :], + } + + if self._interface is not None: + self._attr_extra_state_attributes["Int"] = self._interface + + self._on_icon = icon_on + self._off_icon = icon + + if self._off_icon is not None: + self._attr_icon = self._off_icon + + self._attr_is_on = None + + async def async_added_to_hass(self): + """When entity is added to hass.""" + self._hass.data[DOMAIN][self._gateway_handler.mac][CONF_PLATFORMS][self._platform][self._device_id][CONF_ENTITIES][self._attr_device_class] = self + await self.async_update() + + async def async_will_remove_from_hass(self): + """When entity is removed from hass.""" + if self._attr_device_class in self._hass.data[DOMAIN][self._gateway_handler.mac][CONF_PLATFORMS][self._platform][self._device_id][CONF_ENTITIES]: + del self._hass.data[DOMAIN][self._gateway_handler.mac][CONF_PLATFORMS][self._platform][self._device_id][CONF_ENTITIES][self._attr_device_class] + + async def async_update(self): + """Update the entity. + + Only used by the generic entity update service. + """ + await self._gateway_handler.send_status_request(OWNLightingCommand.status(self._full_where)) + + def handle_event(self, message: OWNLightingEvent): + """Handle an event message.""" + LOGGER.info( + "%s %s", + self._gateway_handler.log_id, + message.human_readable_log, + ) + self._attr_is_on = message.is_on != self._inverted + self.async_schedule_update_ha_state() diff --git a/custom_components/myhome/validate.py b/custom_components/myhome/validate.py index a27b09c..638ba27 100644 --- a/custom_components/myhome/validate.py +++ b/custom_components/myhome/validate.py @@ -350,6 +350,8 @@ def __call__(self, data): Required(CONF_NAME): str, Optional(CONF_ENTITY_NAME): str, Optional(CONF_INVERTED, default=False): Boolean(), + Optional(CONF_ICON): str, + Optional(CONF_ICON_ON): str, Optional(CONF_DEVICE_CLASS): In( [ BinarySensorDeviceClass.BATTERY, From 4ab6fabd24289b53861ebaf307676870b505adfe Mon Sep 17 00:00:00 2001 From: Jacopo Jannone Date: Sun, 19 May 2024 00:34:46 +0200 Subject: [PATCH 06/22] Add support for WHO=4 actuators --- custom_components/myhome/binary_sensor.py | 62 +++++++++++++++++++---- 1 file changed, 51 insertions(+), 11 deletions(-) diff --git a/custom_components/myhome/binary_sensor.py b/custom_components/myhome/binary_sensor.py index 363208e..e66995c 100644 --- a/custom_components/myhome/binary_sensor.py +++ b/custom_components/myhome/binary_sensor.py @@ -14,9 +14,11 @@ from homeassistant.helpers.restore_state import RestoreEntity from OWNd.message import ( + OWNEvent, OWNDryContactEvent, OWNDryContactCommand, OWNLightingCommand, + OWNHeatingCommand, MESSAGE_TYPE_MOTION, MESSAGE_TYPE_PIR_SENSITIVITY, MESSAGE_TYPE_MOTION_TIMEOUT, @@ -119,6 +121,24 @@ async def async_setup_entry(hass, config_entry, async_add_entities): gateway=hass.data[DOMAIN][config_entry.data[CONF_MAC]][CONF_ENTITY], ) _binary_sensors.append(_binary_sensor) + elif _who == 4 and _device_class == BinarySensorDeviceClass.POWER: + _binary_sensor = MyHOMEActuator( + hass=hass, + device_id=_binary_sensor, + who=_configured_binary_sensors[_binary_sensor][CONF_WHO], + where=_configured_binary_sensors[_binary_sensor][CONF_WHERE], + icon=_configured_binary_sensors[_binary_sensor][CONF_ICON], + icon_on=_configured_binary_sensors[_binary_sensor][CONF_ICON_ON], + name=_configured_binary_sensors[_binary_sensor][CONF_NAME], + entity_name=_configured_binary_sensors[_binary_sensor][CONF_ENTITY_NAME], + inverted=_configured_binary_sensors[_binary_sensor][CONF_INVERTED], + interface=_configured_binary_sensors[_binary_sensor][CONF_BUS_INTERFACE] if CONF_BUS_INTERFACE in _configured_binary_sensors[_binary_sensor] else None, + device_class=_device_class, + manufacturer=_configured_binary_sensors[_binary_sensor][CONF_MANUFACTURER], + model=_configured_binary_sensors[_binary_sensor][CONF_DEVICE_MODEL], + gateway=hass.data[DOMAIN][config_entry.data[CONF_MAC]][CONF_ENTITY], + ) + _binary_sensors.append(_binary_sensor) async_add_entities(_binary_sensors) @@ -395,15 +415,24 @@ def __init__( self._attr_unique_id = f"{gateway.mac}-{self._device_id}-{self._attr_device_class}" self._interface = interface - self._full_where = f"{self._where}#4#{self._interface}" if self._interface is not None else self._where - self._attr_extra_state_attributes = { - "A": where[: len(where) // 2], - "PL": where[len(where) // 2 :], - } - - if self._interface is not None: - self._attr_extra_state_attributes["Int"] = self._interface + if self._who == 1: + if self._interface is not None: + self._attr_extra_state_attributes["Int"] = self._interface + self._full_where = f"{self._where}#4#{self._interface}" + else: + self._full_where = self._where + self._attr_extra_state_attributes = { + "A": where[: len(where) // 2], + "PL": where[len(where) // 2 :], + } + elif self._who == 4: + if self._interface is not None: + raise ValueError("Interface cannot be set with WHO=4") + self._attr_extra_state_attributes = { + "Z": where.split("#")[0], + "N": where.split("#")[1], + } self._on_icon = icon_on self._off_icon = icon @@ -428,14 +457,25 @@ async def async_update(self): Only used by the generic entity update service. """ - await self._gateway_handler.send_status_request(OWNLightingCommand.status(self._full_where)) + if self._who == 1: + await self._gateway_handler.send_status_request(OWNLightingCommand.status(self._full_where)) + elif self._who == 4: + await self._gateway_handler.send_status_request(OWNHeatingCommand.actuator_status(self._where)) - def handle_event(self, message: OWNLightingEvent): + def handle_event(self, message: OWNEvent): """Handle an event message.""" LOGGER.info( "%s %s", self._gateway_handler.log_id, message.human_readable_log, ) - self._attr_is_on = message.is_on != self._inverted + + if self._who == 1: + self._attr_is_on = message.is_on != self._inverted + elif self._who == 4: + self._attr_is_on = message.is_active != self._inverted + + if self._off_icon is not None and self._on_icon is not None: + self._attr_icon = self._on_icon if self._attr_is_on else self._off_icon + self.async_schedule_update_ha_state() From 5c68e0e90c7f90ae1f0ffb70d4f2f481a3f1a361 Mon Sep 17 00:00:00 2001 From: Jacopo Jannone Date: Sun, 19 May 2024 00:51:11 +0200 Subject: [PATCH 07/22] Use raw OWNHeatingCommand --- custom_components/myhome/binary_sensor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/myhome/binary_sensor.py b/custom_components/myhome/binary_sensor.py index e66995c..75cf00e 100644 --- a/custom_components/myhome/binary_sensor.py +++ b/custom_components/myhome/binary_sensor.py @@ -460,7 +460,7 @@ async def async_update(self): if self._who == 1: await self._gateway_handler.send_status_request(OWNLightingCommand.status(self._full_where)) elif self._who == 4: - await self._gateway_handler.send_status_request(OWNHeatingCommand.actuator_status(self._where)) + await self._gateway_handler.send_status_request(OWNHeatingCommand(f"*#4*{self._where}*20##")) def handle_event(self, message: OWNEvent): """Handle an event message.""" From 96136994c3436cda17ba13ca8fd890377c434ee0 Mon Sep 17 00:00:00 2001 From: Jacopo Jannone Date: Sun, 19 May 2024 00:58:14 +0200 Subject: [PATCH 08/22] Update validation function --- custom_components/myhome/validate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/myhome/validate.py b/custom_components/myhome/validate.py index 638ba27..bf1c9d0 100644 --- a/custom_components/myhome/validate.py +++ b/custom_components/myhome/validate.py @@ -345,7 +345,7 @@ def __call__(self, data): binary_sensor_schema = MyHomeDeviceSchema( { Required(str): { - Optional(CONF_WHO, default="25"): In(["1", "9", "25"]), + Optional(CONF_WHO, default="25"): In(["1", "4", "9", "25"]), Required(CONF_WHERE): All(Coerce(str), SpecialWhere()), Required(CONF_NAME): str, Optional(CONF_ENTITY_NAME): str, From 98998303ccbf5cdb78790acd118a39d6701dff0d Mon Sep 17 00:00:00 2001 From: Jacopo Jannone Date: Sun, 19 May 2024 01:02:53 +0200 Subject: [PATCH 09/22] SpecialWhere can contain # --- custom_components/myhome/validate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/custom_components/myhome/validate.py b/custom_components/myhome/validate.py index bf1c9d0..63211be 100644 --- a/custom_components/myhome/validate.py +++ b/custom_components/myhome/validate.py @@ -147,10 +147,10 @@ def __init__(self, msg=None): self.msg = msg def __call__(self, v): - if type(v) == str and v.isdigit(): + if type(v) == str and re.match(r"^[0-9#]+$", v): return v else: - raise Invalid(f"Invalid WHERE {v}, it must be a string of digits.") + raise Invalid(f"Invalid WHERE {v}, it must be a string of [0-9#]+.") def __repr__(self): return "Where(%s, msg=%r)" % ("String", self.msg) From 093140b74c7281f01a8997080b982f145f9fca3b Mon Sep 17 00:00:00 2001 From: Jacopo Jannone Date: Sun, 19 May 2024 01:16:29 +0200 Subject: [PATCH 10/22] Fix who as strings, not ints --- custom_components/myhome/binary_sensor.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/custom_components/myhome/binary_sensor.py b/custom_components/myhome/binary_sensor.py index 75cf00e..5b91d57 100644 --- a/custom_components/myhome/binary_sensor.py +++ b/custom_components/myhome/binary_sensor.py @@ -416,7 +416,7 @@ def __init__( self._interface = interface - if self._who == 1: + if self._who == "1": if self._interface is not None: self._attr_extra_state_attributes["Int"] = self._interface self._full_where = f"{self._where}#4#{self._interface}" @@ -426,7 +426,7 @@ def __init__( "A": where[: len(where) // 2], "PL": where[len(where) // 2 :], } - elif self._who == 4: + elif self._who == "4": if self._interface is not None: raise ValueError("Interface cannot be set with WHO=4") self._attr_extra_state_attributes = { @@ -457,9 +457,9 @@ async def async_update(self): Only used by the generic entity update service. """ - if self._who == 1: + if self._who == "1": await self._gateway_handler.send_status_request(OWNLightingCommand.status(self._full_where)) - elif self._who == 4: + elif self._who == "4": await self._gateway_handler.send_status_request(OWNHeatingCommand(f"*#4*{self._where}*20##")) def handle_event(self, message: OWNEvent): @@ -470,9 +470,9 @@ def handle_event(self, message: OWNEvent): message.human_readable_log, ) - if self._who == 1: + if self._who == "1": self._attr_is_on = message.is_on != self._inverted - elif self._who == 4: + elif self._who == "4": self._attr_is_on = message.is_active != self._inverted if self._off_icon is not None and self._on_icon is not None: From 64ff51d093028d392c2b00793f639ffd409a0841 Mon Sep 17 00:00:00 2001 From: Jacopo Jannone Date: Sun, 19 May 2024 18:56:53 +0200 Subject: [PATCH 11/22] Only store extra attribute Z --- custom_components/myhome/binary_sensor.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/custom_components/myhome/binary_sensor.py b/custom_components/myhome/binary_sensor.py index 5b91d57..e751dcd 100644 --- a/custom_components/myhome/binary_sensor.py +++ b/custom_components/myhome/binary_sensor.py @@ -430,8 +430,7 @@ def __init__( if self._interface is not None: raise ValueError("Interface cannot be set with WHO=4") self._attr_extra_state_attributes = { - "Z": where.split("#")[0], - "N": where.split("#")[1], + "Z": where } self._on_icon = icon_on From fe73f9f3bf7f98a6af2a8cdb1114ed49eeae0fa5 Mon Sep 17 00:00:00 2001 From: Jacopo Jannone Date: Sun, 19 May 2024 18:57:12 +0200 Subject: [PATCH 12/22] Fix actuator activation condition --- custom_components/myhome/binary_sensor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/custom_components/myhome/binary_sensor.py b/custom_components/myhome/binary_sensor.py index e751dcd..414cf2c 100644 --- a/custom_components/myhome/binary_sensor.py +++ b/custom_components/myhome/binary_sensor.py @@ -471,8 +471,8 @@ def handle_event(self, message: OWNEvent): if self._who == "1": self._attr_is_on = message.is_on != self._inverted - elif self._who == "4": - self._attr_is_on = message.is_active != self._inverted + elif self._who == "4" and message.dimension == 20: + self._attr_is_on = message.is_active() != self._inverted if self._off_icon is not None and self._on_icon is not None: self._attr_icon = self._on_icon if self._attr_is_on else self._off_icon From 5d206986b8bac15de7203b7bac1576b0c6c5fae4 Mon Sep 17 00:00:00 2001 From: Jacopo Jannone Date: Sun, 19 May 2024 18:57:27 +0200 Subject: [PATCH 13/22] Workaround due to OWNd behavior --- custom_components/myhome/gateway.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/custom_components/myhome/gateway.py b/custom_components/myhome/gateway.py index 74ccf49..97ed188 100644 --- a/custom_components/myhome/gateway.py +++ b/custom_components/myhome/gateway.py @@ -143,6 +143,11 @@ async def listening_loop(self): message = await _event_session.get_next() LOGGER.debug("%s Message received: `%s`", self.log_id, message) + # Workaround due to how the OWNd library creates the entity ID, + # replacing zone=0 with zone=where_param + if isinstance(message, OWNHeatingEvent) and message.where == "0": + message._zone = 0 + if self.generate_events: if isinstance(message, OWNMessage): _event_content = {"gateway": str(self.gateway.host)} From d33ae889e374c8fadbb3850e81e8066e58e6dd9a Mon Sep 17 00:00:00 2001 From: Jacopo Jannone Date: Sun, 19 May 2024 22:33:43 +0200 Subject: [PATCH 14/22] Implement F523 actuator status --- custom_components/myhome/binary_sensor.py | 35 +++++++++++++++++++++++ custom_components/myhome/const.py | 1 + custom_components/myhome/gateway.py | 7 +++++ custom_components/myhome/validate.py | 4 ++- 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/custom_components/myhome/binary_sensor.py b/custom_components/myhome/binary_sensor.py index 414cf2c..969bedc 100644 --- a/custom_components/myhome/binary_sensor.py +++ b/custom_components/myhome/binary_sensor.py @@ -19,6 +19,7 @@ OWNDryContactCommand, OWNLightingCommand, OWNHeatingCommand, + OWNEnergyCommand, MESSAGE_TYPE_MOTION, MESSAGE_TYPE_PIR_SENSITIVITY, MESSAGE_TYPE_MOTION_TIMEOUT, @@ -31,6 +32,7 @@ CONF_ENTITY_NAME, CONF_WHO, CONF_WHERE, + CONF_PHASE, CONF_MANUFACTURER, CONF_DEVICE_MODEL, CONF_DEVICE_CLASS, @@ -109,6 +111,7 @@ async def async_setup_entry(hass, config_entry, async_add_entities): device_id=_binary_sensor, who=_configured_binary_sensors[_binary_sensor][CONF_WHO], where=_configured_binary_sensors[_binary_sensor][CONF_WHERE], + phase=_configured_binary_sensors[_binary_sensor][CONF_PHASE] if CONF_PHASE in _configured_binary_sensors[_binary_sensor] else None, icon=_configured_binary_sensors[_binary_sensor][CONF_ICON], icon_on=_configured_binary_sensors[_binary_sensor][CONF_ICON_ON], name=_configured_binary_sensors[_binary_sensor][CONF_NAME], @@ -127,6 +130,26 @@ async def async_setup_entry(hass, config_entry, async_add_entities): device_id=_binary_sensor, who=_configured_binary_sensors[_binary_sensor][CONF_WHO], where=_configured_binary_sensors[_binary_sensor][CONF_WHERE], + phase=_configured_binary_sensors[_binary_sensor][CONF_PHASE] if CONF_PHASE in _configured_binary_sensors[_binary_sensor] else None, + icon=_configured_binary_sensors[_binary_sensor][CONF_ICON], + icon_on=_configured_binary_sensors[_binary_sensor][CONF_ICON_ON], + name=_configured_binary_sensors[_binary_sensor][CONF_NAME], + entity_name=_configured_binary_sensors[_binary_sensor][CONF_ENTITY_NAME], + inverted=_configured_binary_sensors[_binary_sensor][CONF_INVERTED], + interface=_configured_binary_sensors[_binary_sensor][CONF_BUS_INTERFACE] if CONF_BUS_INTERFACE in _configured_binary_sensors[_binary_sensor] else None, + device_class=_device_class, + manufacturer=_configured_binary_sensors[_binary_sensor][CONF_MANUFACTURER], + model=_configured_binary_sensors[_binary_sensor][CONF_DEVICE_MODEL], + gateway=hass.data[DOMAIN][config_entry.data[CONF_MAC]][CONF_ENTITY], + ) + _binary_sensors.append(_binary_sensor) + elif _who == 18 and _device_class == BinarySensorDeviceClass.POWER: + _binary_sensor = MyHOMEActuator( + hass=hass, + device_id=_binary_sensor, + who=_configured_binary_sensors[_binary_sensor][CONF_WHO], + where=_configured_binary_sensors[_binary_sensor][CONF_WHERE], + phase=_configured_binary_sensors[_binary_sensor][CONF_PHASE] if CONF_PHASE in _configured_binary_sensors[_binary_sensor] else None, icon=_configured_binary_sensors[_binary_sensor][CONF_ICON], icon_on=_configured_binary_sensors[_binary_sensor][CONF_ICON_ON], name=_configured_binary_sensors[_binary_sensor][CONF_NAME], @@ -388,6 +411,7 @@ def __init__( device_id: str, who: str, where: str, + phase: str, inverted: bool, interface: str, device_class: str, @@ -408,6 +432,7 @@ def __init__( ) self._inverted = inverted + self._phase = phase self._attr_device_class = device_class self._attr_name = entity_name if entity_name else self._attr_device_class.replace("_", " ").capitalize() @@ -432,6 +457,12 @@ def __init__( self._attr_extra_state_attributes = { "Z": where } + elif self._who == "18": + if self._interface is not None: + raise ValueError("Interface cannot be set with WHO=18") + self._attr_extra_state_attributes = { + "P": str(int(where) - 70) + } self._on_icon = icon_on self._off_icon = icon @@ -460,6 +491,8 @@ async def async_update(self): await self._gateway_handler.send_status_request(OWNLightingCommand.status(self._full_where)) elif self._who == "4": await self._gateway_handler.send_status_request(OWNHeatingCommand(f"*#4*{self._where}*20##")) + elif self._who == "18": + await self._gateway_handler.send_status_request(OWNEnergyCommand(f"*#18*{self._where}#{self._phase}*71##")) def handle_event(self, message: OWNEvent): """Handle an event message.""" @@ -473,6 +506,8 @@ def handle_event(self, message: OWNEvent): self._attr_is_on = message.is_on != self._inverted elif self._who == "4" and message.dimension == 20: self._attr_is_on = message.is_active() != self._inverted + elif self._who == "18" and message.dimension == 71: + self._attr_is_on = bool(int(message._dimension_value[0])) != self._inverted if self._off_icon is not None and self._on_icon is not None: self._attr_icon = self._on_icon if self._attr_is_on else self._off_icon diff --git a/custom_components/myhome/const.py b/custom_components/myhome/const.py index 9c560b6..f70194b 100644 --- a/custom_components/myhome/const.py +++ b/custom_components/myhome/const.py @@ -30,6 +30,7 @@ CONF_PARENT_ID = "parent_id" CONF_WHO = "who" CONF_WHERE = "where" +CONF_PHASE = "phase" CONF_BUS_INTERFACE = "interface" CONF_ZONE = "zone" CONF_DIMMABLE = "dimmable" diff --git a/custom_components/myhome/gateway.py b/custom_components/myhome/gateway.py index 97ed188..1159126 100644 --- a/custom_components/myhome/gateway.py +++ b/custom_components/myhome/gateway.py @@ -170,6 +170,13 @@ async def listening_loop(self): MyHOMEEntity, ): self.hass.data[DOMAIN][self.mac][CONF_PLATFORMS][SENSOR][message.entity][CONF_ENTITIES][_entity].handle_event(message) + elif BINARY_SENSOR in self.hass.data[DOMAIN][self.mac][CONF_PLATFORMS] and message.entity in self.hass.data[DOMAIN][self.mac][CONF_PLATFORMS][BINARY_SENSOR]: + for _entity in self.hass.data[DOMAIN][self.mac][CONF_PLATFORMS][BINARY_SENSOR][message.entity][CONF_ENTITIES]: + if isinstance( + self.hass.data[DOMAIN][self.mac][CONF_PLATFORMS][BINARY_SENSOR][message.entity][CONF_ENTITIES][_entity], + MyHOMEEntity, + ): + self.hass.data[DOMAIN][self.mac][CONF_PLATFORMS][BINARY_SENSOR][message.entity][CONF_ENTITIES][_entity].handle_event(message) else: continue elif ( diff --git a/custom_components/myhome/validate.py b/custom_components/myhome/validate.py index 63211be..85770bc 100644 --- a/custom_components/myhome/validate.py +++ b/custom_components/myhome/validate.py @@ -35,6 +35,7 @@ CONF_PLATFORMS, CONF_WHO, CONF_WHERE, + CONF_PHASE, CONF_BUS_INTERFACE, CONF_ENTITIES, CONF_ENTITY_NAME, @@ -345,8 +346,9 @@ def __call__(self, data): binary_sensor_schema = MyHomeDeviceSchema( { Required(str): { - Optional(CONF_WHO, default="25"): In(["1", "4", "9", "25"]), + Optional(CONF_WHO, default="25"): In(["1", "4", "9", "18", "25"]), Required(CONF_WHERE): All(Coerce(str), SpecialWhere()), + Optional(CONF_PHASE): str, Required(CONF_NAME): str, Optional(CONF_ENTITY_NAME): str, Optional(CONF_INVERTED, default=False): Boolean(), From 3747a62dd43d16f2db4b270e53ce23ae666f5144 Mon Sep 17 00:00:00 2001 From: Jacopo Jannone Date: Mon, 27 May 2024 23:53:23 +0200 Subject: [PATCH 15/22] Change condition on heating/cooling activation --- custom_components/myhome/climate.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/custom_components/myhome/climate.py b/custom_components/myhome/climate.py index c5a4839..66d5819 100644 --- a/custom_components/myhome/climate.py +++ b/custom_components/myhome/climate.py @@ -418,14 +418,9 @@ def handle_event(self, message: OWNHeatingEvent): message.human_readable_log, ) if message.is_active(): - if self._heating and self._cooling: - if message.is_heating(): - self._attr_hvac_action = HVACAction.HEATING - elif message.is_cooling(): - self._attr_hvac_action = HVACAction.COOLING - elif self._heating: + if self._attr_hvac_mode == HVACMode.HEAT: self._attr_hvac_action = HVACAction.HEATING - elif self._cooling: + if self._attr_hvac_mode == HVACMode.COOL: self._attr_hvac_action = HVACAction.COOLING elif self._attr_hvac_mode == HVACMode.OFF: self._attr_hvac_action = HVACAction.OFF From 1c201485cb5e4e74c25ed58a5340676f2e28fa55 Mon Sep 17 00:00:00 2001 From: Jacopo Jannone Date: Tue, 28 May 2024 00:48:35 +0200 Subject: [PATCH 16/22] Avoid syncing local and master temperature --- custom_components/myhome/climate.py | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/custom_components/myhome/climate.py b/custom_components/myhome/climate.py index 66d5819..570441c 100644 --- a/custom_components/myhome/climate.py +++ b/custom_components/myhome/climate.py @@ -191,7 +191,10 @@ def target_temperature(self) -> float: if self._local_target_temperature is not None: return self._local_target_temperature else: - return self._target_temperature + return ( + (self._target_temperature - self._local_offset) + if self._target_temperature is not None else None + ) async def async_set_hvac_mode(self, hvac_mode): """Set new target hvac mode.""" @@ -293,9 +296,6 @@ def handle_event(self, message: OWNHeatingEvent): message.human_readable_log, ) self._target_temperature = message.set_temperature - self._local_target_temperature = ( - self._target_temperature + self._local_offset - ) elif message.message_type == MESSAGE_TYPE_LOCAL_OFFSET: LOGGER.info( "%s %s", @@ -303,10 +303,6 @@ def handle_event(self, message: OWNHeatingEvent): message.human_readable_log, ) self._local_offset = message.local_offset - if self._target_temperature is not None: - self._local_target_temperature = ( - self._target_temperature + self._local_offset - ) elif message.message_type == MESSAGE_TYPE_LOCAL_TARGET_TEMPERATURE: LOGGER.info( "%s %s", @@ -314,9 +310,6 @@ def handle_event(self, message: OWNHeatingEvent): message.human_readable_log, ) self._local_target_temperature = message.local_set_temperature - self._target_temperature = ( - self._local_target_temperature - self._local_offset - ) elif message.message_type == MESSAGE_TYPE_MODE: if ( message.mode == CLIMATE_MODE_AUTO @@ -408,9 +401,6 @@ def handle_event(self, message: OWNHeatingEvent): self._attr_hvac_mode = HVACMode.OFF self._attr_hvac_action = HVACAction.OFF self._target_temperature = message.set_temperature - self._local_target_temperature = ( - self._target_temperature + self._local_offset - ) elif message.message_type == MESSAGE_TYPE_ACTION: LOGGER.info( "%s %s", From a54fb72d45ebb2c86ad6a0b79a231d5cda0e9be0 Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 28 Aug 2024 11:17:41 +0200 Subject: [PATCH 17/22] Changed version number --- custom_components/myhome/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/myhome/manifest.json b/custom_components/myhome/manifest.json index d9176fc..96da0a5 100644 --- a/custom_components/myhome/manifest.json +++ b/custom_components/myhome/manifest.json @@ -2,7 +2,7 @@ "domain": "myhome", "integration_type": "hub", "name": "MyHOME", - "version": "0.9.4", + "version": "0.9.4-michnovka", "config_flow": true, "documentation": "https://github.com/anotherjulien/MyHOME", "issue_tracker": "https://github.com/anotherjulien/MyHOME/issues", From 654653f9c2affd34b589c080a68275a591b0f95f Mon Sep 17 00:00:00 2001 From: Tomas Date: Wed, 28 Aug 2024 11:41:39 +0200 Subject: [PATCH 18/22] ClimateEntityFeature missing supported attributes --- custom_components/myhome/climate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom_components/myhome/climate.py b/custom_components/myhome/climate.py index c5a4839..9fdfb20 100644 --- a/custom_components/myhome/climate.py +++ b/custom_components/myhome/climate.py @@ -147,7 +147,7 @@ def __init__( self._attr_min_temp = 5 self._attr_max_temp = 40 - self._attr_supported_features = 0 + self._attr_supported_features = ClimateEntityFeature.TURN_ON | ClimateEntityFeature.TURN_OFF self._attr_hvac_modes = [HVACMode.OFF] self._heating = heating self._cooling = cooling From e5de544fbff1d162b742ffb77abfc0aa081ce8be Mon Sep 17 00:00:00 2001 From: Tomas Date: Fri, 30 Aug 2024 19:56:08 +0200 Subject: [PATCH 19/22] Fix crash for unsupported dimension events --- custom_components/myhome/light.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/custom_components/myhome/light.py b/custom_components/myhome/light.py index 91ed3b2..db4fa6b 100644 --- a/custom_components/myhome/light.py +++ b/custom_components/myhome/light.py @@ -217,12 +217,16 @@ def handle_event(self, message: OWNLightingEvent): self._gateway_handler.log_id, message.human_readable_log, ) - self._attr_is_on = message.is_on - if ColorMode.BRIGHTNESS in self._attr_supported_color_modes and message.brightness is not None: - self._attr_brightness_pct = message.brightness - self._attr_brightness = percent_to_eight_bits(message.brightness) - - if self._off_icon is not None and self._on_icon is not None: - self._attr_icon = self._on_icon if self._attr_is_on else self._off_icon - - self.async_schedule_update_ha_state() + #This should be in try block as message.is_on can throw error for unsupported message types (like for some dimensions) + try: + self._attr_is_on = message.is_on + if ColorMode.BRIGHTNESS in self._attr_supported_color_modes and message.brightness is not None: + self._attr_brightness_pct = message.brightness + self._attr_brightness = percent_to_eight_bits(message.brightness) + + if self._off_icon is not None and self._on_icon is not None: + self._attr_icon = self._on_icon if self._attr_is_on else self._off_icon + + self.async_schedule_update_ha_state() + except TypeError: + pass \ No newline at end of file From 5f347a5a36e229ecb3fcf35ce5358edddb4b4621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Garc=C3=ADa?= Date: Sat, 7 Dec 2024 20:25:07 +0100 Subject: [PATCH 20/22] Add error handling for resilience --- custom_components/myhome/gateway.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/custom_components/myhome/gateway.py b/custom_components/myhome/gateway.py index 821a20d..00543be 100644 --- a/custom_components/myhome/gateway.py +++ b/custom_components/myhome/gateway.py @@ -164,7 +164,15 @@ async def listening_loop(self): self.hass.data[DOMAIN][self.mac][CONF_PLATFORMS][SENSOR][message.entity][CONF_ENTITIES][_entity], MyHOMEEntity, ): - self.hass.data[DOMAIN][self.mac][CONF_PLATFORMS][SENSOR][message.entity][CONF_ENTITIES][_entity].handle_event(message) + try: + self.hass.data[DOMAIN][self.mac][CONF_PLATFORMS][SENSOR][message.entity][CONF_ENTITIES][_entity].handle_event(message) + except TypeError: + LOGGER.debug( + "%s Error handling event `%s`", + self.log_id, + message, + ) + pass else: continue elif ( @@ -280,7 +288,15 @@ async def listening_loop(self): EnableCommandButtonEntity, ) ): - self.hass.data[DOMAIN][self.mac][CONF_PLATFORMS][_platform][message.entity][CONF_ENTITIES][_entity].handle_event(message) + try: + self.hass.data[DOMAIN][self.mac][CONF_PLATFORMS][_platform][message.entity][CONF_ENTITIES][_entity].handle_event(message) + except TypeError: + LOGGER.debug( + "%s Error handling event `%s`", + self.log_id, + message, + ) + pass else: LOGGER.debug( From fdfe8eb76bbc01f3e5b6cb1b385e7f2836134c05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Garc=C3=ADa?= Date: Sat, 7 Dec 2024 20:50:19 +0100 Subject: [PATCH 21/22] Remove pass --- custom_components/myhome/gateway.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/custom_components/myhome/gateway.py b/custom_components/myhome/gateway.py index 00543be..06e0ade 100644 --- a/custom_components/myhome/gateway.py +++ b/custom_components/myhome/gateway.py @@ -172,7 +172,6 @@ async def listening_loop(self): self.log_id, message, ) - pass else: continue elif ( @@ -296,7 +295,6 @@ async def listening_loop(self): self.log_id, message, ) - pass else: LOGGER.debug( From 99fb30a9363bda0dece9a96f663bdfe468db18d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nacho=20Garc=C3=ADa?= Date: Thu, 12 Dec 2024 09:37:46 +0100 Subject: [PATCH 22/22] Handle more errors --- custom_components/myhome/gateway.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/custom_components/myhome/gateway.py b/custom_components/myhome/gateway.py index 06e0ade..e836cb1 100644 --- a/custom_components/myhome/gateway.py +++ b/custom_components/myhome/gateway.py @@ -166,8 +166,8 @@ async def listening_loop(self): ): try: self.hass.data[DOMAIN][self.mac][CONF_PLATFORMS][SENSOR][message.entity][CONF_ENTITIES][_entity].handle_event(message) - except TypeError: - LOGGER.debug( + except: + LOGGER.error( "%s Error handling event `%s`", self.log_id, message, @@ -264,7 +264,7 @@ async def listening_loop(self): ) if not is_event: if isinstance(message, OWNLightingEvent) and message.brightness_preset: - if isinstance( + if message.entity in self.hass.data[DOMAIN][self.mac][CONF_PLATFORMS][LIGHT] and isinstance( self.hass.data[DOMAIN][self.mac][CONF_PLATFORMS][LIGHT][message.entity][CONF_ENTITIES][LIGHT], MyHOMEEntity, ): @@ -289,8 +289,8 @@ async def listening_loop(self): ): try: self.hass.data[DOMAIN][self.mac][CONF_PLATFORMS][_platform][message.entity][CONF_ENTITIES][_entity].handle_event(message) - except TypeError: - LOGGER.debug( + except: + LOGGER.error( "%s Error handling event `%s`", self.log_id, message,