Skip to content

Commit

Permalink
Merge pull request #91 from s-kostyuk/thing_events
Browse files Browse the repository at this point in the history
Thing on_update events

This pull request prepares infrastructure for tracking of updates of Things and their properties

- added a protected method called `_apply_update()` for notification of subscribers on Thing updates;
- added a new interface (abstract class) called UpdateCallback which allows to assign callbacks to be
  called on each update of an object (i.e. Thing);
- implemented UpdateCallback interface for a base Thing implementation;
- added `_apply_update()` calls for base Actuator implementation

Closes #12
  • Loading branch information
s-kostyuk committed May 1, 2018
2 parents acbf704 + 46e2c63 commit a59eb29
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 2 deletions.
2 changes: 1 addition & 1 deletion dpl/integrations/abs_actuator.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def _state(self, new_value: 'AbsActuator.States') -> None:
:param new_value: new state value to be set
:return: None
"""
self._last_updated = time.time()
self._apply_update()
self._really_internal_state_value = new_value

@property
Expand Down
43 changes: 42 additions & 1 deletion dpl/things/thing.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
from copy import deepcopy
from types import MappingProxyType
from typing import Mapping, Sequence
import weakref

from typing import Optional, Callable

# Include 3rd-party modules
# Include DPL modules
Expand All @@ -13,9 +16,10 @@
from dpl.things.capabilities.is_available import Available
from dpl.things.capabilities.last_updated import LastUpdated
from .capability_filler_meta import CapabilityFiller
from .update_callback import UpdateCallback


class Thing(BaseEntity, IsEnabled, Available, LastUpdated,
class Thing(BaseEntity, IsEnabled, Available, LastUpdated, UpdateCallback,
metaclass=CapabilityFiller):
"""
Thing is a base class for all connected devices in the system.
Expand Down Expand Up @@ -79,6 +83,8 @@ def __init__(
self._metadata = deepcopy(metadata)
self._last_updated = time.time()
self._is_enabled = False
self._on_update = None
self._weak_self = weakref.proxy(self)

@property
def capabilities(self) -> Sequence[str]: # -> Collection[str]:
Expand Down Expand Up @@ -118,6 +124,29 @@ def last_updated(self) -> float:
"""
return self._last_updated

@property
def on_update(self) -> Optional[Callable]:
"""
Returns a callable that is currently registered to be called on each
update of this object
:return: a callable that is currently registered to be called on each
update of this object; or None if a callable wasn't set yet
"""
return self._on_update

@on_update.setter
def on_update(self, callback: Optional[Callable]) -> None:
"""
Allows to set a callable to be called on each update of this object.
This callable must to accept a reference to the event source (i.e. to
this object)
:param callback: a new callback to be set or None to unset a callback
:return: None
"""
self._on_update = callback

def _check_is_available(self) -> None:
"""
Checks if this thing is available and raises and exception otherwise
Expand All @@ -128,3 +157,15 @@ def _check_is_available(self) -> None:
if not self.is_available:
raise RuntimeError("This thing is unavailable and can't be used at this time")

def _apply_update(self) -> None:
"""
A method to be called after EACH update to ANY of the Thing's field.
Updates the value of last_updated field and calls a callback registered
in on_update property
:return: None
"""
self._last_updated = time.time()

if self._on_update:
self._on_update(self._weak_self)
34 changes: 34 additions & 0 deletions dpl/things/update_callback.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from typing import Optional, Callable


class UpdateCallback(object):
"""
Implementations of UpdateCallback interface has an on_update property
that allows to define a single callback to be called on each update
of a Thing.
A callback to be registered must to accept a single parameter: a weak
reference to the event source (i.e. to the updated Thing)
"""
@property
def on_update(self) -> Optional[Callable]:
"""
Returns a callable that is currently registered to be called on each
update of this object
:return: a callable that is currently registered to be called on each
update of this object; or None if a callable wasn't set yet
"""
raise NotImplementedError()

@on_update.setter
def on_update(self, callback: Optional[Callable]) -> None:
"""
Allows to set a callable to be called on each update of this object.
This callable must to accept a reference to the event source (i.e. to
this object)
:param callback: a new callback to be set or None to unset a callback
:return: None
"""
raise NotImplementedError()

0 comments on commit a59eb29

Please sign in to comment.