Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions home/appliance/sound/player/state/fade_in/callable.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class Elapsed(Callable):
def run(self, event, state):
if event == home.event.elapsed.Event.On:
state = self.get_new_state(state, "off")
state = state.next(home.event.elapsed.Event.Off)
return state


Expand Down
1 change: 1 addition & 0 deletions home/builder/scheduler/trigger/state/entering/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ def _build_args(self, mapping):


from home.builder.scheduler.trigger.state.entering import delay
from home.builder.scheduler.trigger.state.entering import disable_events
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ def _build_args(self, mapping):


from home.builder.scheduler.trigger.state.entering.delay import duration
from home.builder.scheduler.trigger.state.entering.delay import enable_events
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# SPDX-License-Identifier: GPL-3.0-only
#
# automate home devices
#
# Copyright (C) 2021 Maja Massarini

from home.builder.scheduler.trigger.state.entering.delay import Builder as Parent
from home.scheduler.trigger.state.entering.delay.enable_events import Trigger


class Builder(Parent):
TAG_NAME = "state.entering.delay.enable_events.Trigger"

@property
def trigger(self):
return Trigger

def _build_args(self, mapping):
name = mapping["name"]
events = mapping["enable events"]
state = mapping["when appliance state became"]
timeout = mapping["and timeout expires"]
return [name, events, state, timeout]
22 changes: 22 additions & 0 deletions home/builder/scheduler/trigger/state/entering/disable_events.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# SPDX-License-Identifier: GPL-3.0-only
#
# automate home devices
#
# Copyright (C) 2021 Maja Massarini

from home.builder.scheduler.trigger.state.entering import Builder as Parent
from home.scheduler.trigger.state.entering.disable_events import Trigger


class Builder(Parent):
TAG_NAME = "state.entering.disable_events.Trigger"

@property
def trigger(self):
return Trigger

def _build_args(self, mapping):
name = mapping["name"]
events = mapping["disable events"]
state = mapping["when appliance state became"]
return [name, events, state]
82 changes: 56 additions & 26 deletions home/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,11 @@ async def _update_performers_by_protocol_trigger(self, scheduler, trigger):
async def _on_appliance_updated_by_redis(self, scheduler, new_appliance):
old_appliance = self._appliances[new_appliance.name]
old_state, new_state = old_appliance.update(new_appliance)
self._logger.debug("Appliance {} updated by redis".format(new_appliance.name))
self._logger.debug(
"Appliance {} updated by redis ({} -> {})".format(
new_appliance.name, old_state.compute(), new_state.compute()
)
)
for performer in [
performer
for performer in self._performers
Expand All @@ -113,9 +117,10 @@ async def _on_appliance_updated_by_redis(self, scheduler, new_appliance):
)
msgs = performer.execute(old_state, new_state)
if msgs:
self._logger.info(
"Performer {} updated by redis will send {}".format(
performer.name, msgs
self._logger.debug(
"Performer {} sending {} ({} -> {})".format(
performer.name, msgs,
old_state.compute(), new_state.compute()
)
)
for writer in self._protocols_writers:
Expand All @@ -128,9 +133,10 @@ async def _on_performer_updated_by_redis(self, performer, old_state, new_state):
msgs = performer.execute(old_state, new_state)
self._logger.debug("Performer {} updated by redis".format(performer.name))
if msgs:
self._logger.info(
"Performer {} updated by redis will send {}".format(
performer.name, msgs
self._logger.debug(
"Performer {} sending {} ({} -> {})".format(
performer.name, msgs,
old_state.compute(), new_state.compute()
)
)
for writer in self._protocols_writers:
Expand All @@ -148,27 +154,51 @@ async def _run(self, scheduler):
performer, trigger, events = await self._queue.get()
if trigger.is_enabled and events:
try:
msgs, old_state, new_state = performer.notify(events)
self._logger.debug(
"Performer {} notified by Scheduler Trigger {}".format(
performer.name, trigger.name
if isinstance(
trigger,
home.scheduler.trigger.state.entering.disable_events.Trigger,
):
for event in events:
performer.appliance.disable(event)
self._logger.debug(
"Performer {} disabled events {} by Trigger {}".format(
performer.name, events, trigger.name
)
)
)
await self._redis_gateway.on_performer_updated_by_process(
performer, old_state, new_state
)
if msgs:
self._logger.info(
"Performer {} called by Protocol Trigger {} will send {}".format(
performer.name, trigger.name, msgs
elif isinstance(
trigger,
home.scheduler.trigger.date.enable_events.Trigger,
):
for event in events:
performer.appliance.enable(event)
self._logger.debug(
"Performer {} enabled events {} by Trigger {}".format(
performer.name, events, trigger.name
)
)
for writer in self._protocols_writers:
await writer(msgs, performer)
else:
msgs, old_state, new_state = performer.notify(events)
self._logger.debug(
"Performer {} notified by Scheduler Trigger {}".format(
performer.name, trigger.name
)
)
await self._redis_gateway.on_performer_updated_by_process(
performer, old_state, new_state
)
if msgs:
self._logger.info(
"Performer {} called by Protocol Trigger {}"
" will send {}".format(
performer.name, trigger.name, msgs
)
)
for writer in self._protocols_writers:
await writer(msgs, performer)

await self._schedule_by_appliance_state(
scheduler, performer.appliance, old_state, new_state
)
await self._schedule_by_appliance_state(
scheduler, performer.appliance, old_state, new_state
)
except Exception as e:
self._logger.error(e)
if trigger.is_enabled:
Expand All @@ -191,9 +221,9 @@ def create_tasks(self, loop, scheduler):

async def monitor(self):
while True:
self._logger.warning("\n\nNew tasks:\n")
self._logger.debug("\n\nNew tasks:\n")
for task in asyncio.all_tasks():
self._logger.warning(task.get_name())
self._logger.debug(task.get_name())
await asyncio.sleep(180)

def run(self, scheduler):
Expand Down
10 changes: 6 additions & 4 deletions home/redis/gateway/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,13 @@ async def on_performer_updated_by_process(self, performer, old_state, new_state)
def create_tasks(
self, loop, on_appliance_updated_by_redis, on_performer_updated_by_redis
):
for client in self._appliances.values():
for appliance, client in self._appliances.items():
asyncio.get_event_loop().create_task(
client.run(on_appliance_updated_by_redis), name="Appliance updated by redis"
client.run(on_appliance_updated_by_redis),
name="Appliance {} updated by redis".format(appliance.name),
)
for client in self._performers.values():
for performer, client in self._performers.items():
asyncio.get_event_loop().create_task(
client.run(on_performer_updated_by_redis), name="Performer updated by redis"
client.run(on_performer_updated_by_redis),
name="Performer {} updated by redis".format(performer.name),
)
1 change: 1 addition & 0 deletions home/scheduler/trigger/date/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ def __init__(self, name: str, events: Iterable["home.Event"], *args, **kwargs):


from home.scheduler.trigger.date import resettable
from home.scheduler.trigger.date import enable_events
14 changes: 14 additions & 0 deletions home/scheduler/trigger/date/enable_events.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# SPDX-License-Identifier: GPL-3.0-only
#
# automate home devices
#
# Copyright (C) 2021 Maja Massarini

from home.scheduler.trigger.date.resettable import Trigger as Parent


class Trigger(Parent):
"""
A resettable date trigger that, when fired, re-enables events
in the Appliance state machine.
"""
1 change: 1 addition & 0 deletions home/scheduler/trigger/state/entering/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,4 @@ def __str__(self):


from home.scheduler.trigger.state.entering import delay
from home.scheduler.trigger.state.entering import disable_events
1 change: 1 addition & 0 deletions home/scheduler/trigger/state/entering/delay/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,4 @@ def __str__(self):


from home.scheduler.trigger.state.entering.delay import duration
from home.scheduler.trigger.state.entering.delay import enable_events
67 changes: 67 additions & 0 deletions home/scheduler/trigger/state/entering/delay/enable_events.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# SPDX-License-Identifier: GPL-3.0-only
#
# automate home devices
#
# Copyright (C) 2021 Maja Massarini

import copy
import datetime
from typing import Iterable, List, Tuple

from home.scheduler.trigger.date import enable_events as date_enable_events
from home.scheduler.trigger.protocol.delay import Delay
from home.scheduler.trigger.state.entering.delay import Trigger as Parent


class _EnableEventsDelay(Delay):
def fork(
self, performer: "home.Performer"
) -> List[Tuple["home.Performer", "home.scheduler.Trigger"]]:
result = list()
name = (
"date.enable_events.Trigger for parent trigger"
" {} and performer {}".format(self._trigger_name, performer.name)
)
run_date = datetime.datetime.now() + datetime.timedelta(
seconds=self._timeout
)
if name in self._last_resettable_trigger:
self._last_resettable_trigger[name].disable()
self._last_resettable_trigger[name] = date_enable_events.Trigger(
name,
self._scheduler_trigger_events,
run_date=run_date,
timezone=self._timezone,
)
result.append((performer, self._last_resettable_trigger[name]))
return result


class Trigger(Parent):
"""
A **Scheduler Trigger** that, when entering the specified state,
starts a timer and after the given timeout re-enables the specified events
in the Appliance state machine.

>>> import home
>>> off = home.appliance.sound.player.state.off.State()
>>> fade_in = home.appliance.sound.player.state.fade_in.State()
>>> trigger = Trigger("re-enable forced off", [], 'Fade In', 1)
>>> trigger.is_triggered(off, fade_in)
True
"""

def __init__(
self,
name: str,
events: Iterable["home.Event"],
state: str,
timeout_seconds: float,
):
super().__init__(name, events, state, timeout_seconds)
self._delay = _EnableEventsDelay(
"delay trigger for {}".format(name),
copy.deepcopy(events),
timeout_seconds,
self._timezone,
)
14 changes: 14 additions & 0 deletions home/scheduler/trigger/state/entering/disable_events.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# SPDX-License-Identifier: GPL-3.0-only
#
# automate home devices
#
# Copyright (C) 2021 Maja Massarini

from home.scheduler.trigger.state.entering import Trigger as Parent


class Trigger(Parent):
"""
A **Scheduler Trigger** that, when entering the specified state,
disables the specified events in the Appliance state machine.
"""
Loading