Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
e5a5d27
Added openenig and closing time attributes
andrea-parisi Apr 27, 2024
bba3c64
Add the support to move a cover to a position without advanced device
andrea-parisi May 4, 2024
4cadffe
Changed version number
andrea-parisi May 4, 2024
3b03182
Fix string format bug in logging
May 13, 2024
c1e7884
Implement raw actuators as binary sensors
May 13, 2024
4ab6fab
Add support for WHO=4 actuators
May 18, 2024
5c68e0e
Use raw OWNHeatingCommand
May 18, 2024
9613699
Update validation function
May 18, 2024
9899830
SpecialWhere can contain #
May 18, 2024
093140b
Fix who as strings, not ints
May 18, 2024
64ff51d
Only store extra attribute Z
May 19, 2024
fe73f9f
Fix actuator activation condition
May 19, 2024
5d20698
Workaround due to OWNd behavior
May 19, 2024
d33ae88
Implement F523 actuator status
May 19, 2024
3747a62
Change condition on heating/cooling activation
May 27, 2024
1c20148
Avoid syncing local and master temperature
May 27, 2024
3a34057
Merge pull request #1 from jacopo-j/master
michnovka Aug 28, 2024
8e0c6e1
Merge pull request #2 from andrea-parisi/master
michnovka Aug 28, 2024
a54fb72
Changed version number
michnovka Aug 28, 2024
654653f
ClimateEntityFeature missing supported attributes
michnovka Aug 28, 2024
f0a42b1
Merge pull request #3 from michnovka/fix/ClimateEntityFeature-on-off-…
michnovka Aug 28, 2024
e5de544
Fix crash for unsupported dimension events
michnovka Aug 30, 2024
4325b01
Merge pull request #4 from michnovka/fix-invalid-dimensions-is-on
michnovka Aug 30, 2024
5f347a5
Add error handling for resilience
nachogarcia Dec 7, 2024
fdfe8eb
Remove pass
nachogarcia Dec 7, 2024
99fb30a
Handle more errors
nachogarcia Dec 12, 2024
ffb3255
Merge branch 'master' into error_handling_events
nachogarcia Jan 16, 2025
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
179 changes: 179 additions & 0 deletions custom_components/myhome/binary_sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@
from homeassistant.helpers.restore_state import RestoreEntity

from OWNd.message import (
OWNEvent,
OWNDryContactEvent,
OWNDryContactCommand,
OWNLightingCommand,
OWNHeatingCommand,
OWNEnergyCommand,
MESSAGE_TYPE_MOTION,
MESSAGE_TYPE_PIR_SENSITIVITY,
MESSAGE_TYPE_MOTION_TIMEOUT,
Expand All @@ -29,10 +32,14 @@
CONF_ENTITY_NAME,
CONF_WHO,
CONF_WHERE,
CONF_PHASE,
CONF_MANUFACTURER,
CONF_DEVICE_MODEL,
CONF_DEVICE_CLASS,
CONF_INVERTED,
CONF_BUS_INTERFACE,
CONF_ICON,
CONF_ICON_ON,
DOMAIN,
LOGGER,
)
Expand Down Expand Up @@ -98,6 +105,63 @@ 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],
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 == 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],
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],
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)

Expand Down Expand Up @@ -334,3 +398,118 @@ 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,
phase: 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._phase = phase

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

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
}
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

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.
"""
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(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."""
LOGGER.info(
"%s %s",
self._gateway_handler.log_id,
message.human_readable_log,
)

if self._who == "1":
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

self.async_schedule_update_ha_state()
29 changes: 7 additions & 22 deletions custom_components/myhome/climate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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."""
Expand Down Expand Up @@ -293,30 +296,20 @@ 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",
self._gateway_handler.log_id,
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",
self._gateway_handler.log_id,
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
Expand Down Expand Up @@ -408,24 +401,16 @@ 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",
self._gateway_handler.log_id,
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
Expand Down
3 changes: 3 additions & 0 deletions custom_components/myhome/const.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -46,3 +47,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"
Loading