Skip to content

Commit

Permalink
This is a combination of 2 commits.
Browse files Browse the repository at this point in the history
Avoid multiple threads on the same scene by
using change all eval related methods to async

remove unused import
  • Loading branch information
cayossarian committed Dec 31, 2024
1 parent 6232651 commit c4319a9
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 18 deletions.
45 changes: 33 additions & 12 deletions custom_components/stateful_scenes/StatefulScenes.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from typing import Any

from homeassistant.core import Event, EventStateChangedData, HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.event import async_call_later
from homeassistant.helpers.template import area_id, area_name

Expand Down Expand Up @@ -324,9 +325,10 @@ def update_callback(self, event: Event[EventStateChangedData]):

async def async_evaluate_scene_state(self):
"""Evaluate scene state immediately."""
await self.hass.async_add_executor_job(self.check_all_states)
_LOGGER.debug("[Scene: %s] Starting scene evaluation", self.name)
await self.async_check_all_states()
if self.schedule_update:
await self.hass.async_add_executor_job(self.schedule_update, True)
self.schedule_update(True)

async def async_timer_evaluate_scene_state(self, _now):
"""Handle Callback from HA after expiration of SceneEvaluationTimer."""
Expand All @@ -337,6 +339,8 @@ async def async_timer_evaluate_scene_state(self, _now):
def is_interesting_update(self, old_state, new_state):
"""Check if the state change is interesting."""
if old_state is None:
if new_state is None:
_LOGGER.warning("New State is None and Old State is None")
return True
if not self.compare_values(old_state.state, new_state.state):
return True
Expand All @@ -354,11 +358,31 @@ def is_interesting_update(self, old_state, new_state):
return True
return False

def check_state(self, entity_id, new_state):
async def async_check_state(self, entity_id, new_state):
"""Check if entity's current state matches the scene's defined state."""
if new_state is None:
_LOGGER.warning("Entity not found: %s", entity_id)
return False
# Check if entity exists in registry
# Get entity registry directly
registry = er.async_get(self.hass)
entry = registry.async_get(entity_id)

if entry is None:
_LOGGER.debug(
"[Scene: %s] Entity %s not found in registry.",
self.name,
entity_id,
)
return False

# Check if entity exists in state
new_state = self.hass.states.get(entity_id)
if new_state is None:
_LOGGER.debug(
"[Scene: %s] Entity %s not found in state.",
self.name,
entity_id,
)
return False

if self.ignore_unavailable and new_state.state == "unavailable":
return None
Expand Down Expand Up @@ -407,7 +431,7 @@ def check_state(self, entity_id, new_state):
)
return True

def check_all_states(self):
async def async_check_all_states(self):
"""Check the state of the scene.
If all entities are in the desired state, the scene is on. If any entity is not
Expand All @@ -416,14 +440,11 @@ def check_all_states(self):
"""
for entity_id in self.entities:
state = self.hass.states.get(entity_id)
self.states[entity_id] = self.check_state(entity_id, state)
self.states[entity_id] = await self.async_check_state(entity_id, state)

states = [state for state in self.states.values() if state is not None]

if not states:
self._is_on = False
else:
self._is_on = all(states)
result = all(states) if states else False
self._is_on = result

def store_entity_state(self, entity_id, state=None):
"""Store the state of an entity.
Expand Down
12 changes: 6 additions & 6 deletions custom_components/stateful_scenes/switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,19 +164,19 @@ async def async_added_to_hass(self) -> None:
"""Validate and set the actual scene state on restart."""
await super().async_added_to_hass()

def _validate_scene_state():
self._scene.check_all_states()
async def async_validate_scene_state(_now=None):
await self._scene.async_check_all_states()
self._is_on = self._scene.is_on
self.schedule_update_ha_state()
self.async_write_ha_state()

self.hass.loop.call_later(1, _validate_scene_state)
await async_validate_scene_state()

def update(self) -> None:
async def async_update(self) -> None:
"""Fetch new state data for this light.
This is the only method that should fetch new data for Home Assistant.
"""
self._scene.check_all_states()
await self._scene.async_check_all_states()
self._is_on = self._scene.is_on

def register_callback(self) -> None:
Expand Down

0 comments on commit c4319a9

Please sign in to comment.