improved trigger code

This commit is contained in:
2024-12-17 20:48:00 +01:00
parent 35c86ffbd3
commit 494ba80e88
6 changed files with 66 additions and 26 deletions
+3
View File
@@ -10,6 +10,7 @@ import xmltodict
class TriggerTuple(NamedTuple): class TriggerTuple(NamedTuple):
name: str name: str
type: str
interval_in_seconds: int interval_in_seconds: int
governor: str governor: str
custom_config: OrderedDict[str, Any] custom_config: OrderedDict[str, Any]
@@ -68,6 +69,7 @@ class Config:
_trigger_elements = self._get_multiple_elements_by_xpath('triggers/trigger') _trigger_elements = self._get_multiple_elements_by_xpath('triggers/trigger')
for trigger_elem in _trigger_elements: for trigger_elem in _trigger_elements:
name = self._get_single_text_value_from_xpath('name', trigger_elem) name = self._get_single_text_value_from_xpath('name', trigger_elem)
type = self._get_single_text_value_from_xpath('type', trigger_elem)
interval_in_seconds = self._get_single_text_value_from_xpath('intervalInSeconds', trigger_elem) interval_in_seconds = self._get_single_text_value_from_xpath('intervalInSeconds', trigger_elem)
governor = self._get_single_text_value_from_xpath('governor', trigger_elem) governor = self._get_single_text_value_from_xpath('governor', trigger_elem)
try: try:
@@ -78,6 +80,7 @@ class Config:
custom_config = OrderedDict({}) custom_config = OrderedDict({})
_new_trigger = TriggerTuple( _new_trigger = TriggerTuple(
name=name, name=name,
type=type,
interval_in_seconds=int(interval_in_seconds), interval_in_seconds=int(interval_in_seconds),
governor=governor, governor=governor,
custom_config=custom_config custom_config=custom_config
+3
View File
@@ -5,16 +5,19 @@
<triggers> <triggers>
<trigger> <trigger>
<name>test_trigger1</name> <name>test_trigger1</name>
<type>run_once</type>
<intervalInSeconds>1</intervalInSeconds> <intervalInSeconds>1</intervalInSeconds>
<governor>ondemand</governor> <governor>ondemand</governor>
</trigger> </trigger>
<trigger> <trigger>
<name>test_trigger2</name> <name>test_trigger2</name>
<type>run_once</type>
<intervalInSeconds>3</intervalInSeconds> <intervalInSeconds>3</intervalInSeconds>
<governor>conservative</governor> <governor>conservative</governor>
</trigger> </trigger>
<trigger> <trigger>
<name>roon</name> <name>roon</name>
<type>callback</type>
<intervalInSeconds>2</intervalInSeconds> <intervalInSeconds>2</intervalInSeconds>
<governor>performance</governor> <governor>performance</governor>
<customConfig> <customConfig>
+15 -3
View File
@@ -10,16 +10,28 @@ class TriggerScheduler(AppClass):
super().__init__(_config) super().__init__(_config)
self.tasks: list[Task] = [] self.tasks: list[Task] = []
async def trigger(self, _trigger: Trigger) -> None: async def callback_trigger(self, _trigger: Trigger) -> None:
"""Run a callback_trigger with a specific name and check its status at a given interval."""
await _trigger.async_run()
while True:
trigger_state = "active" if _trigger.active else "not active"
self.log.debug("Checking state of trigger %s, state: %s", _trigger.name, trigger_state)
await asyncio.sleep(_trigger.config.interval_in_seconds)
async def run_once_trigger(self, _trigger: Trigger) -> None:
"""Run a trigger with a specific name at a given interval.""" """Run a trigger with a specific name at a given interval."""
while True: while True:
self.log.debug("Trigger %s is running", _trigger.name)
_trigger.run() _trigger.run()
trigger_state = "active" if _trigger.active else "not active"
self.log.debug("Checking state of trigger %s, state: %s", _trigger.name, trigger_state)
await asyncio.sleep(_trigger.config.interval_in_seconds) await asyncio.sleep(_trigger.config.interval_in_seconds)
def start_trigger(self, _trigger: Trigger) -> None: def start_trigger(self, _trigger: Trigger) -> None:
"""Start a new trigger.""" """Start a new trigger."""
task = asyncio.create_task(self.trigger(_trigger)) if _trigger.config.type == "callback":
task = asyncio.create_task(self.callback_trigger(_trigger))
else:
task = asyncio.create_task(self.run_once_trigger(_trigger))
self.tasks.append(task) self.tasks.append(task)
async def stop_triggers(self) -> None: async def stop_triggers(self) -> None:
+17 -5
View File
@@ -6,6 +6,7 @@ from exceptions import MissingConfig, TriggerImportError
class Trigger(AppClass): class Trigger(AppClass):
def __init__(self, _config: Config) -> None: def __init__(self, _config: Config) -> None:
super().__init__(_config) super().__init__(_config)
self.active: bool = False
@property @property
def name(self) -> str: def name(self) -> str:
@@ -23,8 +24,19 @@ class Trigger(AppClass):
raise MissingConfig(f"Trigger {self.name} hasn't been properly configured") raise MissingConfig(f"Trigger {self.name} hasn't been properly configured")
return _config return _config
def run(self) -> bool: def run(self) -> None:
# return True if Trigger is valid # this is the main function of the trigger class and has to stay active by using a while True loop
raise TriggerImportError(
"the Trigger class can't used directly, but must be inherited in a trigger specific class" while True:
) raise TriggerImportError(
"the Trigger class can't used directly, but must be inherited in a trigger specific class"
)
async def async_run(self) -> None:
# this is the main function of the trigger class and can be used to execute a callback function
while True:
raise TriggerImportError(
"the Trigger class can't used directly, but must be inherited in a trigger specific class"
)
+22 -14
View File
@@ -2,7 +2,7 @@ from trigger import Trigger
from config import Config from config import Config
from roonapi import RoonApi, RoonDiscovery # type: ignore from roonapi import RoonApi, RoonDiscovery # type: ignore
from functools import cached_property from functools import cached_property
from typing import Any, NamedTuple from typing import Any, NamedTuple, Union
from pathlib import Path from pathlib import Path
@@ -16,10 +16,21 @@ class RoonServer(NamedTuple):
class RoonTrigger(Trigger): class RoonTrigger(Trigger):
def __init__(self, _config: Config) -> None: def __init__(self, _config: Config) -> None:
super().__init__(_config) super().__init__(_config)
self.set_status()
@property def set_status(self) -> None:
def playing(self) -> bool: zones_state = list()
return False
for zone_id in self.roonapi.zones:
self.log.debug(self.roonapi.zones[zone_id])
zone = self.roonapi.zones[zone_id]
self.log.debug("zone_id: %s zone_info: %s" , zone_id, zone)
zone_name = zone['display_name']
zone_state = zone['state']
self.log.info("zone_id: %s name: %s, state: %s" , zone_id, zone_name, zone_state)
zones_state.append(zone_state)
self.active = True if "playing" in zones_state else False
@cached_property @cached_property
def appinfo(self) -> dict[str, Any]: def appinfo(self) -> dict[str, Any]:
@@ -69,15 +80,12 @@ class RoonTrigger(Trigger):
_roonapi = RoonApi(self.appinfo, self.token, self.server.ip, self.server.port, True) _roonapi = RoonApi(self.appinfo, self.token, self.server.ip, self.server.port, True)
return _roonapi return _roonapi
def my_state_callback(self, event, changed_ids): def roon_state_callback(self, event: str, changed_ids: Union[str, list[str]]) -> None:
"""Call when something changes in roon.""" """Call when something changes in roon."""
self.log.info("my_state_callback event:%s changed_ids: %s" % (event, changed_ids)) self.log.debug(
for zone_id in changed_ids: "%s event: %s changed_ids: %s" , RoonTrigger.roon_state_callback.__qualname__, event, changed_ids
zone = self.roonapi.zones[zone_id] )
self.log.info("zone_id:%s zone_info: %s" % (zone_id, zone)) self.set_status()
def run(self) -> bool:
self.roonapi.register_state_callback(self.my_state_callback)
return False
async def async_run(self) -> None:
self.roonapi.register_state_callback(self.roon_state_callback, "zones_changed")
+4 -2
View File
@@ -1,10 +1,12 @@
from trigger import Trigger from trigger import Trigger
from config import Config from config import Config
import random
class TestTrigger1(Trigger): class TestTrigger1(Trigger):
def __init__(self, _config: Config) -> None: def __init__(self, _config: Config) -> None:
super().__init__(_config) super().__init__(_config)
def run(self) -> bool: def run(self) -> None:
choices = [False, True]
self.log.info("run check code of %s", self.__class__.__name__) self.log.info("run check code of %s", self.__class__.__name__)
return False self.active = random.choice(choices)