Skip to content

Commit

Permalink
Merge pull request #86 from s-kostyuk/finalized_capabilities
Browse files Browse the repository at this point in the history
Finalized capabilities (1/n)

This pull request finalizes the naming scheme for Capabilities and adds two new important Capabilities to the list: OnOff anf OpenClosed
  • Loading branch information
s-kostyuk committed Mar 20, 2018
2 parents 5ecb2e9 + 4e42a7a commit a20aeee
Show file tree
Hide file tree
Showing 13 changed files with 136 additions and 42 deletions.
8 changes: 4 additions & 4 deletions dpl/integrations/abs_actuator.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@
from dpl.utils.empty_mapping import EMPTY_MAPPING

# Include DPL modules
from dpl.things.capabilities.i_state import IState
from dpl.things.capabilities.i_actuator import (
IActuator,
from dpl.things.capabilities.has_state import HasState
from dpl.things.capabilities.actuator import (
Actuator,
UnsupportedCommandError,
UnacceptableCommandArgumentsError
)
from dpl.things.thing import Thing, TDomainId, Connection


class AbsActuator(Thing, IActuator, IState):
class AbsActuator(Thing, Actuator, HasState):
"""
Actuator is an abstraction of devices that can 'act', perform some commands
and change their states after that.
Expand Down
10 changes: 2 additions & 8 deletions dpl/integrations/abs_slider.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
# Include standard modules
from enum import Enum
from typing import Tuple

# Include 3rd-party modules
# Include DPL modules
from dpl.things.capabilities.open_closed import OpenClosed
from .abs_actuator import AbsActuator


class AbsSlider(AbsActuator):
class AbsSlider(AbsActuator, OpenClosed):
"""
Slider is an abstraction of real-life object, that can be in one of two
stable states: 'opened' and 'closed' and also can be two transition states:
'opening' and 'closing'
"""
class States(Enum):
closed = 0b00
opening = 0b01
closing = 0b10
opened = 0b11
unknown = None

@property
def commands(self) -> Tuple[str, ...]:
Expand Down
12 changes: 11 additions & 1 deletion dpl/integrations/abs_switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@

# Include 3rd-party modules
# Include DPL modules
from dpl.things.capabilities.on_off import OnOff
from .abs_actuator import AbsActuator


class AbsSwitch(AbsActuator):
class AbsSwitch(AbsActuator, OnOff):
"""
Switch is an abstraction of objects with two only available states: 'on'
and 'off'. Like simple light bulb, power socket, relay or fan with
Expand Down Expand Up @@ -35,6 +36,15 @@ def is_active(self):
:return: True if state == 'on', false otherwise
"""
return self.is_powered_on

@property
def is_powered_on(self) -> bool:
"""
Indicates if this device powered on or not
:return: True if this device is powered on, False otherwise.
"""
return self.state == self.States.on

def activate(self) -> None:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from dpl.utils.empty_mapping import EMPTY_MAPPING

from .i_state import IState
from .has_state import HasState


class UnsupportedCommandError(ValueError):
Expand All @@ -24,9 +24,9 @@ class UnacceptableCommandArgumentsError(Exception):
pass


class IActuator(IState):
class Actuator(HasState):
"""
IActuator capability is usually mapped to Actuators.
Actuator capability is usually mapped to Actuators.
Devices with this capability are capable to act, i.e.
perform some actions in the real world like playing
music and changing tracks, turning power on and off,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from enum import Enum

from .is_active import IsActive

class IState(object):

class HasState(IsActive):
"""
This interface allows a read-only access to states of a Thing.
Expand Down Expand Up @@ -38,15 +40,3 @@ def state(self) -> 'States':
:return: an instance of self.State
"""
raise NotImplementedError()

@property
def is_active(self) -> bool:
"""
Returns if this object is currently in one of the
'active' states (like 'on' for lighting, 'open'
for a door and 'playing' for player)
:return: True if object is in any of 'active' states,
False otherwise
"""
raise NotImplementedError()
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
TValueType = TypeVar['TValueType']


class IValue(Generic[TValueType]):
class HasValue(Generic[TValueType]):
"""
Objects with IValue capability are usually mapped to
Objects with HasValue capability are usually mapped to
sensors. They allow to read the current measurements
like the current temperature, humidity or brightness
levels.
Expand Down
41 changes: 41 additions & 0 deletions dpl/things/capabilities/is_active.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from enum import Enum


class IsActive(object):
"""
This interface allows a read-only access to is_active status of a Thing.
This interface must to be provided if only only if the current state of
a Thing can be directly stated as 'active' (i.e. working, acting, turned
on, playing) and 'inactive' (not working, not acting, turned off, stopped).
Otherwise it's not recommended to implement an IsActive interface.
is_active field value if a binary value with True mapped to 'active' and
False mapped to 'inactive'. It's recommended to map uncertain or unknown
(null) state to the 'inactive' (False) value.
The value of is_active field can't be directly changed by user but
Actuators are allowed to provide some additional methods to switch between
'active' and 'inactive' states. Also such Actuators are must to provide
such methods as 'activate', 'deactivate' and 'toggle' to switch between the
opposite states.
Things are must to preserve their last known state even after
they become unavailable. And all client classes must to be ready
to this fact (i.e. analyze a time of the last update in last_updated
property, analyze an availability status in is_available property
and so on).
"""

@property
def is_active(self) -> bool:
"""
Returns if this object is currently in one of the
'active' states (like 'on' for lighting, 'open'
for a door and 'playing' for player)
:return: True if object is in any of 'active' states,
False otherwise
"""
raise NotImplementedError()
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
class IAvailable(object):
class Available(object):
"""
Instances which provide IAvailable capability provides
Instances which provide Available capability provides
their availability status: is they are available for
communication or not - i.e. they are explicitly disabled
(see IEnabled capability), lost, faulted, have a
(see IsEnabled capability), lost, faulted, have a
discharged battery and so on).
"""
@property
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
class IEnabled(object):
class IsEnabled(object):
"""
Things with IEnabled capability can be enabled or disabled.
Things with IsEnabled capability can be enabled or disabled.
In 'disabled' state object becomes unavailable for any
communication and is allowed to disconnect, terminate current
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
class ILastUpdated(object):
class LastUpdated(object):
"""
ILastUpdated capability allows to determine a moment
LastUpdated capability allows to determine a moment
in time when the properties of a Thing was altered
for the last time.
"""
Expand Down
24 changes: 24 additions & 0 deletions dpl/things/capabilities/on_off.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
class OnOff(object):
"""
On/Off devices are devices that can be either powered “on” or “off”.
The current state of those devices can be determined by the value of
the is_power_on field.
Actuator On/Off devices are able to be turned on and off with the on and
off commands correspondingly. Actuator On/Off devices are MUST to provide
``on`` and ``off`` commands. And usually can be toggled between two states
with a ``toggle`` command.
If the device provides both on_off and is_active capabilities, then the
on state is usually mapped to true value of is_active field and off state
is mapped to false. on command is also mapped to the activate and off
command is mapped to the deactivate command.
"""
@property
def is_powered_on(self) -> bool:
"""
Indicates if this device powered on or not
:return: True if this device is powered on, False otherwise.
"""
raise NotImplementedError()
35 changes: 35 additions & 0 deletions dpl/things/capabilities/open_closed.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from enum import Enum

from .has_state import HasState


class OpenClosed(HasState):
"""
Open/Closed devices are devices that can be in either "open" or
"closed" state. The current state of those devices can be determined bу
the value of the ``state`` field. In addition to the "open" and "closed"
states there are two transitional states possible: "opening" and "closing".
Actuator Open/Closed devices are MUST to implement to ``open`` and
``close`` commands to open or close a Thing correspondingly.
If the device provides both ``open_closed`` and ``is_active`` capabilities,
then the ``open`` and ``opening`` states are usually mapped to ``true``
value of ``is_active`` field and ``close`` with ``closing`` states are
mapped to ``false``. Also generic ``activate`` and ``deactivate`` commands
are available for such devices with ``activate`` mapped to ``open``,
``deactivate`` mapped to ``close`` and ``toggle`` toggles between the
opposite states (from ``open`` to ``closed``, from ``closed`` to ``open``,
from ``opening`` to ``closed``, from ``closing`` to ``opened``).
"""
class States(Enum):
"""
Possible states of the thing: 'open', 'closed' and 'unknown'.
"""
closed = 0b00
opening = 0b01
closing = 0b10
opened = 0b11
unknown = None

# Everything else is inherited from HasState Capability
8 changes: 4 additions & 4 deletions dpl/things/thing.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
from dpl.model.domain_id import TDomainId
from dpl.model.base_entity import BaseEntity
from dpl.connections import Connection
from dpl.things.capabilities.i_enabled import IEnabled
from dpl.things.capabilities.i_available import IAvailable
from dpl.things.capabilities.i_last_updated import ILastUpdated
from dpl.things.capabilities.is_enabled import IsEnabled
from dpl.things.capabilities.is_available import Available
from dpl.things.capabilities.last_updated import LastUpdated


class Thing(BaseEntity, IEnabled, IAvailable, ILastUpdated):
class Thing(BaseEntity, IsEnabled, Available, LastUpdated):
"""
Thing is a base class for all connected devices in the system.
Expand Down

0 comments on commit a20aeee

Please sign in to comment.