Skip to content

Commit

Permalink
Merge pull request #70 from spacemanspiff2007/Develop
Browse files Browse the repository at this point in the history
 0.7.2
  • Loading branch information
spacemanspiff2007 authored Sep 3, 2019
2 parents 6a6f9e8 + 45b69b9 commit f193226
Show file tree
Hide file tree
Showing 12 changed files with 230 additions and 136 deletions.
2 changes: 1 addition & 1 deletion HABApp/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__VERSION__ = '0.7.1'
__VERSION__ = '0.7.2'
2 changes: 1 addition & 1 deletion HABApp/core/items/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def __init__(self, name: str, state=None):
self.last_update: datetime.datetime = _now

def set_state(self, new_state) -> bool:
"""Set a new state without creating events
"""Set a new state without creating events on the event bus
:param new_state: new state
:return: True if state has changed
Expand Down
20 changes: 13 additions & 7 deletions HABApp/rule/rule_parameter.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import typing
import warnings

from ..rule_manager import RuleParameters

Expand All @@ -16,35 +17,40 @@ def __init__(self, rule_parameters: RuleParameters, filename: str, *keys, defaul
# as a convenience try to create the file and the file structure
self.__parameters.add_param(self.filename, *self.keys, default_value=default_value)

def get_value(self):
@property
def value(self):
return self.__parameters.get_param(self.filename, *self.keys)

def get_value(self):
warnings.warn("The 'get_value' method is deprecated, use 'value' instead", DeprecationWarning, 2)
return self.value

def __eq__(self, other):
return self.get_value() == other
return self.value == other

def __lt__(self, other):
if not isinstance(other, (int, float)):
return NotImplemented

return self.get_value() < other
return self.value < other

def __le__(self, other):
if not isinstance(other, (int, float)):
return NotImplemented

return self.get_value() <= other
return self.value <= other

def __ge__(self, other):
if not isinstance(other, (int, float)):
return NotImplemented

return self.get_value() >= other
return self.value >= other

def __gt__(self, other):
if not isinstance(other, (int, float)):
return NotImplemented

return self.get_value() > other
return self.value > other

def __repr__(self):
return f'<RuleParameter file: {self.filename}, keys: {self.keys}, value: {self.get_value()}'
return f'<RuleParameter file: {self.filename}, keys: {self.keys}, value: {self.value}'
13 changes: 12 additions & 1 deletion HABApp/rule/scheduler/scheduled_cb.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,23 @@ def get_next_call(self):
return self.next_call

def check_due(self, now : datetime):
"""Check whether the callback is due for execution
:param now:
:return:
"""

self.is_due = True if self.next_call <= now else False
if self.is_finished:
self.is_due = False

return self.is_due

def execute(self):
def execute(self) -> bool:
"""Try to execute callback. If the callback is not due yet or execution has already finished nothing will happen
:return: True if callback has been executed else False
"""
if not self.is_due or self.is_finished:
return False

Expand All @@ -66,4 +75,6 @@ def execute(self):
return True

def cancel(self):
""" Cancel execution
"""
self.is_finished = True
2 changes: 1 addition & 1 deletion HABApp/util/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
from .period_counter import PeriodCounter
from .threshold import Threshold
from .statistics import Statistics
from .prioritized_value import PrioritizedValue
from .multi_value import MultiValue
123 changes: 123 additions & 0 deletions HABApp/util/multi_value.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import datetime
import typing
from threading import Lock


class ValueWithPriority:
def __init__(self, parent, initial_value=None):

assert isinstance(parent, MultiValue), type(parent)
self.__parent: MultiValue = parent

self.__value = None
self.__enabled = False

#: Timestamp of the last update/enable of this value
self.last_update: datetime.datetime = datetime.datetime.now()

# do not call callback for initial value
if initial_value is not None:
self.__enabled = True
self.__value = initial_value

@property
def value(self):
"""Returns the current value"""
return self.__value

@property
def enabled(self) -> bool:
"""Returns if the value is enabled"""
return self.__enabled

def set_value(self, value):
"""Set new value and recalculate overall value
:param value: new value
"""
self.__enabled = True if value is not None else False
self.__value = value

self.last_update = datetime.datetime.now()

self.__parent.recalculate_value(self)

def set_enabled(self, value: bool):
"""Enable or disable this value and recalculate overall value
:param value: True/False
"""
assert value is True or value is False, value
self.__enabled = value

self.last_update = datetime.datetime.now()

self.__parent.recalculate_value(self)

def __str__(self):
return str(self.__value)

def __repr__(self):
return f'<{self.__class__.__name__} enabled: {self.__enabled}, value: {self.__value}>'


class MultiValue:
"""Thread safe value prioritizer"""

def __init__(self, on_value_change):
"""
:param on_value_change: Callback with one arg which will be called on every change
"""
self.on_value_change = on_value_change

self.__value = None

self.__children: typing.Dict[int, ValueWithPriority] = {}
self.__lock = Lock()

@property
def value(self):
"""Returns the current value"""
return self.__value

def get_create_value(self, priority: int, initial_value=None) -> ValueWithPriority:
""" Create a new instance which can be used to set values
:param priority: priority of the value
:param initial_value: initial value
"""
assert isinstance(priority, int), type(priority)

if priority in self.__children:
return self.__children[priority]

self.__children[priority] = ret = ValueWithPriority(self, initial_value)
return ret

def recalculate_value(self, child):
"""Recalculate the output value and call the registered callback (if output has changed)
:param child: child that changed
:return: output value
"""

# recalculate value
new_value = None

with self.__lock:
for priority, child in sorted(self.__children.items()):
assert isinstance(child, ValueWithPriority)

if not child.enabled:
continue
new_value = child.value

value_changed = new_value != self.__value
self.__value = new_value

# Notify that the value has changed
if value_changed:
self.on_value_change(new_value)

return new_value
77 changes: 0 additions & 77 deletions HABApp/util/prioritized_value.py

This file was deleted.

22 changes: 19 additions & 3 deletions _doc/rule.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,24 @@ Rule
.. _datetime.time: <https://docs.python.org/3/library/datetime.html#time-objects>
.. _datetime.timedelta: <https://docs.python.org/3/library/datetime.html#timedelta-objects>


Interacting with items
------------------------------
Items are like variables. They have a name and a state (which can be anything).
Items from openhab use the item name from openhab. Items from MQTT use the topic as item name.
Items from openhab use the item name from openhab and get created when HABApp successfully connects to
openhab or when the openhab configuration changes.
Items from MQTT use the topic as item name and get created as soon as a message gets processed.

Some item types provide convenience functions, so it is advised to always set the correct item type.
Use the class factory `get_create_item` to get the item by name.

The preferred way to interact with items is through the class factory `get_create_item` since this provides type hints::

from HABApp.core.items import Item
my_item = Item.get_create_item('MyItem')

If an item value gets set there will be a :class:`~HABApp.core.ValueUpdateEvent` on the event bus.
If it changes there will be additionally a :class:`~HABApp.core.ValueChangeEvent`, too.


.. list-table::
:widths: auto
:header-rows: 1
Expand Down Expand Up @@ -66,6 +73,10 @@ It is possible to check the item value by comparing it::
if my_item.state == 5:
# do sth

.. autoclass:: HABApp.core.items.Item
:members:


Events
------------------------------

Expand Down Expand Up @@ -140,6 +151,11 @@ Do not use `time.sleep` but rather :meth:`~HABApp.Rule.run_in`.
* - :meth:`~HABApp.Rule.run_on_day_of_week`
- Run a function at a specific time on specific days of the week

All functions return an instance of ScheduledCallback

.. autoclass:: HABApp.rule.scheduler.ScheduledCallback
:members:

Parameters
------------------------------
Parameters are values which can easily be changed without having to reload the rules.
Expand Down
Loading

0 comments on commit f193226

Please sign in to comment.