Skip to content

Commit

Permalink
Align callback naming (#1491)
Browse files Browse the repository at this point in the history
* Align callback naming

* Update device.py

* move last_updated + last_refreshed to CallbackEntity

* Update update.py
  • Loading branch information
SukramJ authored Apr 9, 2024
1 parent bf578a5 commit 2725862
Show file tree
Hide file tree
Showing 10 changed files with 115 additions and 87 deletions.
4 changes: 4 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# Version 2024.4.5 (2024-04-09)

- Align callback naming

# Version 2024.4.4 (2024-04-09)

- Unify entity update/refresh events
Expand Down
60 changes: 30 additions & 30 deletions hahomematic/central/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,16 +144,16 @@ def __init__(self, central_config: CentralConfig) -> None:
self.last_events: Final[dict[str, datetime]] = {}
# Signature: (name, *args)
# e.g. DEVICES_CREATED, HUB_REFRESHED
self._callback_system_event: Final[set[Callable]] = set()
self._system_event_callbacks: Final[set[Callable]] = set()
# Signature: (interface_id, channel_address, parameter, value)
# Re-Fired events from CCU for parameter updates
self._callback_entity_event: Final[set[Callable]] = set()
self._entity_event_callbacks: Final[set[Callable]] = set()
# Signature: (interface_id, entity)
# Fires parameter data updates as events with entity.
self._callback_entity_data_event: Final[set[Callable]] = set()
self._entity_data_event_callbacks: Final[set[Callable]] = set()
# Signature: (event_type, event_data)
# Events like INTERFACE, KEYPRESS, ...
self._callback_ha_event: Final[set[Callable]] = set()
self._ha_event_callbacks: Final[set[Callable]] = set()

self.json_rpc_client: Final[JsonRpcAioHttpClient] = central_config.create_json_rpc_client()

Expand Down Expand Up @@ -263,7 +263,7 @@ def add_sysvar_entity(self, sysvar_entity: GenericSystemVariable) -> None:
def remove_sysvar_entity(self, name: str) -> None:
"""Remove a sysvar entity."""
if (sysvar_entity := self.get_sysvar_entity(name=name)) is not None:
sysvar_entity.fire_entity_removed_callback()
sysvar_entity.fire_device_removed_callback()
del self._sysvar_entities[name]

@property
Expand All @@ -278,7 +278,7 @@ def add_program_button(self, program_button: HmProgramButton) -> None:
def remove_program_button(self, pid: str) -> None:
"""Remove a program button."""
if (program_button := self.get_program_button(pid=pid)) is not None:
program_button.fire_entity_removed_callback()
program_button.fire_device_removed_callback()
del self._program_buttons[pid]

@property
Expand Down Expand Up @@ -1091,37 +1091,37 @@ async def clear_caches(self) -> None:
self.device_details.clear()
self.data_cache.clear()

def register_ha_event_callback(self, callback_handler: Callable) -> None:
def register_ha_event_callback(self, ha_event_callback: Callable) -> None:
"""Register ha_event callback in central."""
self._callback_ha_event.add(callback_handler)
self._ha_event_callbacks.add(ha_event_callback)

def unregister_ha_event_callback(self, callback_handler: Callable) -> None:
def unregister_ha_event_callback(self, ha_event_callback: Callable) -> None:
"""RUn register ha_event callback in central."""
if callback_handler in self._callback_ha_event:
self._callback_ha_event.remove(callback_handler)
if ha_event_callback in self._ha_event_callbacks:
self._ha_event_callbacks.remove(ha_event_callback)

def fire_ha_event_callback(self, event_type: EventType, event_data: dict[str, str]) -> None:
"""
Fire ha_event callback in central.
# Events like INTERFACE, KEYPRESS, ...
"""
for callback_handler in self._callback_ha_event:
for callback_handler in self._ha_event_callbacks:
try:
callback_handler(event_type, event_data)
except Exception as ex:
_LOGGER.error(
"FIRE_HA_EVENT_CALLBACK: Unable to call handler: %s", reduce_args(args=ex.args)
)

def register_entity_event_callback(self, callback_handler: Callable) -> None:
def register_entity_event_callback(self, entity_event_callback: Callable) -> None:
"""Register entity_event callback in central."""
self._callback_entity_event.add(callback_handler)
self._entity_event_callbacks.add(entity_event_callback)

def unregister_entity_event_callback(self, callback_handler: Callable) -> None:
def unregister_entity_event_callback(self, entity_event_callback: Callable) -> None:
"""Un register entity_event callback in central."""
if callback_handler in self._callback_entity_event:
self._callback_entity_event.remove(callback_handler)
if entity_event_callback in self._entity_event_callbacks:
self._entity_event_callbacks.remove(entity_event_callback)

def fire_entity_event_callback(
self, interface_id: str, channel_address: str, parameter: str, value: Any
Expand All @@ -1132,7 +1132,7 @@ def fire_entity_event_callback(
Not used by HA.
Re-Fired events from CCU for parameter updates.
"""
for callback_handler in self._callback_entity_event:
for callback_handler in self._entity_event_callbacks:
try:
callback_handler(interface_id, channel_address, parameter, value)
except Exception as ex:
Expand All @@ -1141,14 +1141,14 @@ def fire_entity_event_callback(
reduce_args(args=ex.args),
)

def register_entity_data_event_callback(self, callback_handler: Callable) -> None:
def register_entity_data_event_callback(self, entity_data_event_callback: Callable) -> None:
"""Register entity_event callback in central."""
self._callback_entity_data_event.add(callback_handler)
self._entity_data_event_callbacks.add(entity_data_event_callback)

def unregister_entity_data_event_callback(self, callback_handler: Callable) -> None:
def unregister_entity_data_event_callback(self, entity_data_event_callback: Callable) -> None:
"""Un register entity_event callback in central."""
if callback_handler in self._callback_entity_data_event:
self._callback_entity_data_event.remove(callback_handler)
if entity_data_event_callback in self._entity_data_event_callbacks:
self._entity_data_event_callbacks.remove(entity_data_event_callback)

def fire_entity_data_event_callback(self, interface_id: str, entity: BaseEntity) -> None:
"""
Expand All @@ -1157,7 +1157,7 @@ def fire_entity_data_event_callback(self, interface_id: str, entity: BaseEntity)
Not used by HA.
Fires parameter data updates as events with entity.
"""
for callback_handler in self._callback_entity_data_event:
for callback_handler in self._entity_data_event_callbacks:
try:
callback_handler(interface_id, entity)
except Exception as ex:
Expand All @@ -1166,22 +1166,22 @@ def fire_entity_data_event_callback(self, interface_id: str, entity: BaseEntity)
reduce_args(args=ex.args),
)

def register_system_event_callback(self, callback_handler: Callable) -> None:
def register_system_event_callback(self, system_event_callback: Callable) -> None:
"""Register system_event callback in central."""
self._callback_system_event.add(callback_handler)
self._system_event_callbacks.add(system_event_callback)

def unregister_system_event_callback(self, callback_handler: Callable) -> None:
def unregister_system_event_callback(self, system_event_callback: Callable) -> None:
"""Un register system_event callback in central."""
if callback_handler in self._callback_system_event:
self._callback_system_event.remove(callback_handler)
if system_event_callback in self._system_event_callbacks:
self._system_event_callbacks.remove(system_event_callback)

def fire_system_event_callback(self, system_event: SystemEvent, **kwargs: Any) -> None:
"""
Fire system_event callback in central.
e.g. DEVICES_CREATED, HUB_REFRESHED
"""
for callback_handler in self._callback_system_event:
for callback_handler in self._system_event_callbacks:
try:
callback_handler(system_event, **kwargs)
except Exception as ex:
Expand Down
41 changes: 24 additions & 17 deletions hahomematic/platforms/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def __init__(self, central: hmcu.CentralUnit, interface_id: str, device_address:
self._generic_events: Final[dict[tuple[str, str], GenericEvent]] = {}
self._last_updated: datetime = INIT_DATETIME
self._forced_availability: ForcedDeviceAvailability = ForcedDeviceAvailability.NOT_SET
self._update_callbacks: Final[list[Callable]] = []
self._device_updated_callbacks: Final[list[Callable]] = []
self._firmware_update_callbacks: Final[list[Callable]] = []
self._device_type: Final = str(
self.central.device_descriptions.get_device_parameter(
Expand Down Expand Up @@ -350,7 +350,9 @@ def add_entity(self, entity: CallbackEntity) -> None:
self.central.add_event_subscription(entity=entity)
if isinstance(entity, GenericEntity):
self._generic_entities[(entity.channel_address, entity.parameter)] = entity
self.register_update_callback(update_callback=entity.fire_entity_updated_callback)
self.register_device_updated_callback(
device_updated_callback=entity.fire_entity_updated_callback
)
if isinstance(entity, hmce.CustomEntity):
self._custom_entities[entity.channel_no] = entity
if isinstance(entity, GenericEvent):
Expand All @@ -362,12 +364,14 @@ def remove_entity(self, entity: CallbackEntity) -> None:
self.central.remove_event_subscription(entity=entity)
if isinstance(entity, GenericEntity):
del self._generic_entities[(entity.channel_address, entity.parameter)]
self.unregister_update_callback(update_callback=entity.fire_entity_updated_callback)
self.unregister_device_updated_callback(
device_updated_callback=entity.fire_entity_updated_callback
)
if isinstance(entity, hmce.CustomEntity):
del self._custom_entities[entity.channel_no]
if isinstance(entity, GenericEvent):
del self._generic_events[(entity.channel_address, entity.parameter)]
entity.fire_entity_removed_callback()
entity.fire_device_removed_callback()

def clear_collections(self) -> None:
"""Remove entities from collections and central."""
Expand All @@ -383,15 +387,18 @@ def clear_collections(self) -> None:
self.remove_entity(custom_entity)
self._custom_entities.clear()

def register_update_callback(self, update_callback: Callable) -> None:
def register_device_updated_callback(self, device_updated_callback: Callable) -> None:
"""Register update callback."""
if callable(update_callback) and update_callback not in self._update_callbacks:
self._update_callbacks.append(update_callback)
if (
callable(device_updated_callback)
and device_updated_callback not in self._device_updated_callbacks
):
self._device_updated_callbacks.append(device_updated_callback)

def unregister_update_callback(self, update_callback: Callable) -> None:
def unregister_device_updated_callback(self, device_updated_callback: Callable) -> None:
"""Remove update callback."""
if update_callback in self._update_callbacks:
self._update_callbacks.remove(update_callback)
if device_updated_callback in self._device_updated_callbacks:
self._device_updated_callbacks.remove(device_updated_callback)

def register_firmware_update_callback(self, firmware_update_callback: Callable) -> None:
"""Register firmware update callback."""
Expand Down Expand Up @@ -518,8 +525,8 @@ def refresh_firmware_data(self) -> None:
or old_firmware_update_state != self._firmware_update_state
or old_firmware_updatable != self._firmware_updatable
):
for _firmware_callback in self._firmware_update_callbacks:
_firmware_callback()
for callback_handler in self._firmware_update_callbacks:
callback_handler()

async def update_firmware(self, refresh_after_update_intervals: tuple[int, ...]) -> bool:
"""Update the firmware of the homematic device."""
Expand Down Expand Up @@ -566,16 +573,16 @@ async def reload_paramset_descriptions(self) -> None:
await self.central.paramset_descriptions.save()
for entity in self.generic_entities:
entity.update_parameter_data()
self.fire_update_device_callback()
self.fire_device_updated_callback()

def fire_update_device_callback(self, *args: Any) -> None:
def fire_device_updated_callback(self, *args: Any) -> None:
"""Do what is needed when the state of the device has been updated."""
self._set_last_updated()
for _callback in self._update_callbacks:
for callback_handler in self._device_updated_callbacks:
try:
_callback(*args)
callback_handler(*args)
except Exception as ex:
_LOGGER.warning("FIRE_UPDATE_DEVICE failed: %s", reduce_args(args=ex.args))
_LOGGER.warning("FIRE_DEVICE_UPDATED failed: %s", reduce_args(args=ex.args))

def __str__(self) -> str:
"""Provide some useful information."""
Expand Down
74 changes: 42 additions & 32 deletions hahomematic/platforms/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,10 @@ def __init__(self, central: hmcu.CentralUnit, unique_id: str) -> None:
self._central: Final = central
self._unique_id: Final = unique_id
self._entity_updated_callbacks: dict[Callable, str] = {}
self._entity_removed_callbacks: list[Callable] = []
self._device_removed_callbacks: list[Callable] = []
self._custom_id: str | None = None
self._last_updated: datetime = INIT_DATETIME
self._last_refreshed: datetime = INIT_DATETIME

@property
@abstractmethod
Expand All @@ -136,6 +138,16 @@ def central(self) -> hmcu.CentralUnit:
def full_name(self) -> str:
"""Return the full name of the entity."""

@value_property
def last_updated(self) -> datetime:
"""Return the last updated datetime value."""
return self._last_updated

@value_property
def last_refreshed(self) -> datetime:
"""Return the last refreshed datetime value."""
return self._last_refreshed

@config_property
@abstractmethod
def name(self) -> str | None:
Expand Down Expand Up @@ -204,34 +216,43 @@ def unregister_entity_updated_callback(
if self.custom_id == custom_id:
self._custom_id = None

def register_entity_removed_callback(self, entity_removed_callback: Callable) -> None:
"""Register the entity removed callback."""
def register_device_removed_callback(self, device_removed_callback: Callable) -> None:
"""Register the device removed callback."""
if (
callable(entity_removed_callback)
and entity_removed_callback not in self._entity_removed_callbacks
callable(device_removed_callback)
and device_removed_callback not in self._device_removed_callbacks
):
self._entity_removed_callbacks.append(entity_removed_callback)
self._device_removed_callbacks.append(device_removed_callback)

def unregister_entity_removed_callback(self, entity_removed_callback: Callable) -> None:
"""Unregister the entity removed callback."""
if entity_removed_callback in self._entity_removed_callbacks:
self._entity_removed_callbacks.remove(entity_removed_callback)
def unregister_device_removed_callback(self, device_removed_callback: Callable) -> None:
"""Unregister the device removed callback."""
if device_removed_callback in self._device_removed_callbacks:
self._device_removed_callbacks.remove(device_removed_callback)

def fire_entity_updated_callback(self, *args: Any, **kwargs: Any) -> None:
"""Do what is needed when the value of the entity has been updated/refreshed."""
for _callback in self._entity_updated_callbacks:
for callback_handler in self._entity_updated_callbacks:
try:
_callback(*args, **kwargs)
callback_handler(*args, **kwargs)
except Exception as ex:
_LOGGER.warning("FIRE_entity_updated_EVENT failed: %s", reduce_args(args=ex.args))

def fire_entity_removed_callback(self, *args: Any) -> None:
def fire_device_removed_callback(self, *args: Any) -> None:
"""Do what is needed when the entity has been removed."""
for _callback in self._entity_removed_callbacks:
for callback_handler in self._device_removed_callbacks:
try:
_callback(*args)
callback_handler(*args)
except Exception as ex:
_LOGGER.warning("FIRE_ENTITY_REMOVED_EVENT failed: %s", reduce_args(args=ex.args))
_LOGGER.warning("FIRE_DEVICE_REMOVED_EVENT failed: %s", reduce_args(args=ex.args))

def _set_last_updated(self, now: datetime = datetime.now()) -> None:
"""Set last_update to current datetime."""
self._last_updated = now
self._set_last_refreshed(now=now)

def _set_last_refreshed(self, now: datetime = datetime.now()) -> None:
"""Set last_update to current datetime."""
self._last_refreshed = now


class BaseEntity(CallbackEntity, PayloadMixin):
Expand Down Expand Up @@ -667,13 +688,11 @@ def write_value(self, value: Any) -> tuple[ParameterT | None, ParameterT | None]
new_value = self._convert_value(value)
if old_value == new_value:
self._set_last_refreshed()
self.fire_entity_updated_callback(is_refresh=True)
return (old_value, new_value)

self._old_value = old_value
self._value = new_value
self._state_uncertain = False
self._set_last_updated()
else:
self._set_last_updated()
self._old_value = old_value
self._value = new_value
self._state_uncertain = False
self.fire_entity_updated_callback()
return (old_value, new_value)

Expand Down Expand Up @@ -730,15 +749,6 @@ def get_event_data(self, value: Any = None) -> dict[str, Any]:
event_data[EVENT_VALUE] = value
return cast(dict[str, Any], EVENT_DATA_SCHEMA(event_data))

def _set_last_updated(self, now: datetime = datetime.now()) -> None:
"""Set last_update to current datetime."""
self._last_updated = now
self._set_last_refreshed(now=now)

def _set_last_refreshed(self, now: datetime = datetime.now()) -> None:
"""Set last_update to current datetime."""
self._last_refreshed = now


class CallParameterCollector:
"""Create a Paramset based on given generic entities."""
Expand Down
1 change: 1 addition & 0 deletions hahomematic/platforms/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ def event(self, value: Any) -> None:
"""Handle event for which this handler has subscribed."""
if self.event_type in ENTITY_EVENTS:
self.fire_entity_updated_callback(parameter=self.parameter.lower())
self._set_last_updated()
self.fire_event(value)

def fire_event(self, value: Any) -> None:
Expand Down
2 changes: 1 addition & 1 deletion hahomematic/platforms/generic/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def event(self, value: Any) -> None:
Parameter.UN_REACH,
Parameter.STICKY_UN_REACH,
):
self._device.fire_update_device_callback(self._unique_id)
self._device.fire_device_updated_callback(self._unique_id)
self._central.fire_ha_event_callback(
event_type=EventType.DEVICE_AVAILABILITY,
event_data=self.get_event_data(new_value),
Expand Down
Loading

0 comments on commit 2725862

Please sign in to comment.