Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use thread-safe method schedule_update_ha_state to write ha state #646

Merged
merged 4 commits into from Apr 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion changelog.md
@@ -1,4 +1,4 @@
# Version 1.59.0 (2024-04-02)
# Version 1.59.0 (2024-04-0)

- Bump hahomematic to 2024.4.1
- Remove support for python 3.11
Expand All @@ -9,6 +9,7 @@
- Use more list comprehension
- Add icons to services
- Use ConfigFlowResult
- Use thread-safe method schedule_update_ha_state to write ha state

# Version 1.58.0 (2024-03-10)

Expand Down
9 changes: 4 additions & 5 deletions custom_components/homematicip_local/event.py
Expand Up @@ -115,20 +115,19 @@ async def async_added_to_hass(self) -> None:

for event in self._hm_channel_events:
event.register_entity_updated_callback(
entity_updated_callback=self._async_event_changed, custom_id=self.entity_id
entity_updated_callback=self._event_changed, custom_id=self.entity_id
)
event.register_entity_removed_callback(
entity_removed_callback=self._async_device_removed
)

@callback
def _async_event_changed(self, *args: Any, **kwargs: Any) -> None:
def _event_changed(self, *args: Any, **kwargs: Any) -> None:
"""Handle device state changes."""
# Don't update disabled entities
if self.enabled:
self._trigger_event(event_type=kwargs["parameter"])
_LOGGER.debug("Device event fired %s", self.name)
self.async_write_ha_state()
self.schedule_update_ha_state()
else:
_LOGGER.debug(
"Device event for %s not fired. Entity is disabled",
Expand All @@ -140,7 +139,7 @@ async def async_will_remove_from_hass(self) -> None:
# Remove callback from device.
for event in self._hm_channel_events:
event.unregister_entity_updated_callback(
entity_updated_callback=self._async_event_changed, custom_id=self.entity_id
entity_updated_callback=self._event_changed, custom_id=self.entity_id
)
event.unregister_entity_removed_callback(
entity_removed_callback=self._async_device_removed
Expand Down
26 changes: 12 additions & 14 deletions custom_components/homematicip_local/generic_entity.py
Expand Up @@ -193,10 +193,10 @@ async def async_added_to_hass(self) -> None:
"""Register callbacks and load initial data."""
if isinstance(self._hm_entity, CallbackEntity):
self._hm_entity.register_entity_updated_callback(
entity_updated_callback=self._async_entity_changed, custom_id=self.entity_id
entity_updated_callback=self._entity_changed, custom_id=self.entity_id
)
self._hm_entity.register_entity_refreshed_callback(
entity_refreshed_callback=self._async_entity_refreshed, custom_id=self.entity_id
entity_refreshed_callback=self._entity_refreshed, custom_id=self.entity_id
)
self._hm_entity.register_entity_removed_callback(
entity_removed_callback=self._async_device_removed
Expand All @@ -214,25 +214,24 @@ async def async_added_to_hass(self) -> None:
self._hm_entity.full_name,
)

@callback
def _async_entity_changed(self, *args: Any, **kwargs: Any) -> None:
def _entity_changed(self, *args: Any, **kwargs: Any) -> None:
"""Handle device state changes."""
# Don't update disabled entities
if self.enabled:
_LOGGER.debug("Device changed event fired for %s", self._hm_entity.full_name)
self.async_write_ha_state()
self.schedule_update_ha_state()
else:
_LOGGER.debug(
"Device changed event for %s not fired. Entity is disabled",
self._hm_entity.full_name,
)

def _async_entity_refreshed(self, *args: Any, **kwargs: Any) -> None:
def _entity_refreshed(self, *args: Any, **kwargs: Any) -> None:
"""Handle device state refreshes."""
# Don't update disabled entities
if self.enabled:
_LOGGER.debug("Device refreshed event fired for %s", self._hm_entity.full_name)
self.async_write_ha_state()
self.schedule_update_ha_state()
else:
_LOGGER.debug(
"Device refreshed event for %s not fired. Entity is disabled",
Expand All @@ -248,10 +247,10 @@ async def async_will_remove_from_hass(self) -> None:
"""Run when hmip device will be removed from hass."""
# Remove callback from device.
self._hm_entity.unregister_entity_updated_callback(
entity_updated_callback=self._async_entity_changed, custom_id=self.entity_id
entity_updated_callback=self._entity_changed, custom_id=self.entity_id
)
self._hm_entity.unregister_entity_refreshed_callback(
entity_refreshed_callback=self._async_entity_refreshed, custom_id=self.entity_id
entity_refreshed_callback=self._entity_refreshed, custom_id=self.entity_id
)
self._hm_entity.unregister_entity_removed_callback(
entity_removed_callback=self._async_device_removed
Expand Down Expand Up @@ -335,7 +334,7 @@ async def async_added_to_hass(self) -> None:
"""Register callbacks and load initial data."""
if isinstance(self._hm_hub_entity, CallbackEntity):
self._hm_hub_entity.register_entity_updated_callback(
entity_updated_callback=self._async_hub_entity_changed, custom_id=self.entity_id
entity_updated_callback=self._hub_entity_changed, custom_id=self.entity_id
)
self._hm_hub_entity.register_entity_removed_callback(
entity_removed_callback=self._async_hub_entity_removed
Expand All @@ -345,19 +344,18 @@ async def async_will_remove_from_hass(self) -> None:
"""Run when hmip sysvar entity will be removed from hass."""
# Remove callbacks.
self._hm_hub_entity.unregister_entity_updated_callback(
entity_updated_callback=self._async_hub_entity_changed, custom_id=self.entity_id
entity_updated_callback=self._hub_entity_changed, custom_id=self.entity_id
)
self._hm_hub_entity.unregister_entity_removed_callback(
entity_removed_callback=self._async_hub_entity_removed
)

@callback
def _async_hub_entity_changed(self, *args: Any, **kwargs: Any) -> None:
def _hub_entity_changed(self, *args: Any, **kwargs: Any) -> None:
"""Handle sysvar entity state changes."""
# Don't update disabled entities
if self.enabled:
_LOGGER.debug("Sysvar changed event fired for %s", self.name)
self.async_write_ha_state()
self.schedule_update_ha_state()
else:
_LOGGER.debug(
"Sysvar changed event for %s not fired. Sysvar entity is disabled",
Expand Down
2 changes: 1 addition & 1 deletion custom_components/homematicip_local/manifest.json
Expand Up @@ -10,7 +10,7 @@
"iot_class": "local_push",
"issue_tracker": "https://github.com/danielperna84/hahomematic/issues",
"loggers": ["hahomematic"],
"requirements": ["hahomematic==2024.4.1"],
"requirements": ["hahomematic==2024.4.2"],
"ssdp": [
{
"manufacturer": "EQ3",
Expand Down
9 changes: 4 additions & 5 deletions custom_components/homematicip_local/update.py
Expand Up @@ -129,19 +129,18 @@ async def async_update(self) -> None:
async def async_added_to_hass(self) -> None:
"""Register callbacks and load initial data."""
self._hm_entity.register_entity_updated_callback(
entity_updated_callback=self._async_entity_changed, custom_id=self.entity_id
entity_updated_callback=self._entity_changed, custom_id=self.entity_id
)
self._hm_entity.register_entity_removed_callback(
entity_removed_callback=self._async_device_removed
)

@callback
def _async_entity_changed(self, *args: Any, **kwargs: Any) -> None:
def _entity_changed(self, *args: Any, **kwargs: Any) -> None:
"""Handle device state changes."""
# Don't update disabled entities
if self.enabled:
_LOGGER.debug("Update state changed event fired for %s", self.name)
self.async_write_ha_state()
self.schedule_update_ha_state()
else:
_LOGGER.debug(
"Update state changed event for %s not fired. Entity is disabled",
Expand All @@ -152,7 +151,7 @@ async def async_will_remove_from_hass(self) -> None:
"""Run when hmip device will be removed from hass."""
# Remove callback from device.
self._hm_entity.unregister_entity_updated_callback(
entity_updated_callback=self._async_entity_changed, custom_id=self.entity_id
entity_updated_callback=self._entity_changed, custom_id=self.entity_id
)
self._hm_entity.unregister_entity_removed_callback(
entity_removed_callback=self._async_device_removed
Expand Down
4 changes: 2 additions & 2 deletions requirements_test.txt
Expand Up @@ -2,11 +2,11 @@

async-upnp-client
coverage==7.4.4
hahomematic==2024.4.1
hahomematic==2024.4.2
homeassistant==2024.4.1
mypy==1.9.0
pip==24.0
pre-commit==3.6.2
pre-commit==3.7.0
pydevccu==0.1.8
pylint-strict-informational==0.1
pylint==3.1.0
Expand Down
3 changes: 3 additions & 0 deletions tests/test_binary_sensor.py
Expand Up @@ -32,12 +32,15 @@ async def test_hmbinarysensor(
assert ha_state.state == STATE_OFF

control.central.event(const.INTERFACE_ID, "VCU5864966:1", "STATE", 1)
await hass.async_block_till_done()
assert hass.states.get(entity_id).state == STATE_ON

control.central.event(const.INTERFACE_ID, "VCU5864966:1", "STATE", 0)
await hass.async_block_till_done()
assert hass.states.get(entity_id).state == STATE_OFF

control.central.event(const.INTERFACE_ID, "VCU5864966:1", "STATE", None)
await hass.async_block_till_done()
assert hass.states.get(entity_id).state == STATE_OFF


Expand Down
4 changes: 4 additions & 0 deletions tests/test_sensor.py
Expand Up @@ -28,9 +28,11 @@ async def test_sensor_trans(factory: helper.Factory) -> None:
assert ha_state.state == STATE_UNKNOWN

control.central.event(const.INTERFACE_ID, "VCU7837366:1", "Taupunkt", 1)
await hass.async_block_till_done()
assert hass.states.get(entity_id).state == "1.0"

control.central.event(const.INTERFACE_ID, "VCU7837366:1", "Taupunkt", 0)
await hass.async_block_till_done()
assert hass.states.get(entity_id).state == "0.0"


Expand All @@ -47,7 +49,9 @@ async def test_sensor_to_trans(factory: helper.Factory) -> None:
assert ha_state.state == STATE_UNKNOWN

control.central.event(const.INTERFACE_ID, "VCU7837366:1", "Abs_Luftfeuchte", 1)
await hass.async_block_till_done()
assert hass.states.get(entity_id).state == "1.0"

control.central.event(const.INTERFACE_ID, "VCU7837366:1", "Abs_Luftfeuchte", 0)
await hass.async_block_till_done()
assert hass.states.get(entity_id).state == "0.0"
2 changes: 2 additions & 0 deletions tests/test_switch.py
Expand Up @@ -31,12 +31,14 @@ async def test_switch(factory: helper.Factory) -> None:
assert ha_state.state == STATE_OFF

control.central.event(const.INTERFACE_ID, "VCU2128127:4", "STATE", 1)
await hass.async_block_till_done()
assert hass.states.get(entity_id).state == STATE_ON
assert hm_entity.turn_on.call_count == 0
await hass.services.async_call("switch", "turn_on", {"entity_id": entity_id}, blocking=True)
assert hm_entity.turn_on.call_count == 1

control.central.event(const.INTERFACE_ID, "VCU2128127:4", "STATE", 0)
await hass.async_block_till_done()
assert hass.states.get(entity_id).state == STATE_OFF
assert hm_entity.turn_off.call_count == 0
await hass.services.async_call("switch", "turn_off", {"entity_id": entity_id}, blocking=True)
Expand Down