Skip to content

Commit

Permalink
0.12.3 (#113)
Browse files Browse the repository at this point in the history
* Added support for persistence data
* Fixes for astral 2.1
  • Loading branch information
spacemanspiff2007 authored Feb 16, 2020
1 parent 5176019 commit bb2f72b
Show file tree
Hide file tree
Showing 9 changed files with 160 additions and 23 deletions.
2 changes: 1 addition & 1 deletion HABApp/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__VERSION__ = '0.12.2'
__VERSION__ = '0.12.3'
18 changes: 11 additions & 7 deletions HABApp/config/_conf_location.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,20 @@ class Location(ConfigContainer):

def __init__(self):
super().__init__()
self.astral: _astral.Location = None

self._astral_location: _astral.LocationInfo
self.astral_observer: _astral.Observer

def on_all_values_set(self):
tz = tzlocal.get_localzone()
tz_name = str(tz)
log.debug(f'Local Timezone: {tz_name}')

self.astral = _astral.Location()
self.astral.name = 'HABApp'
self.astral.latitude = self.latitude
self.astral.longitude = self.longitude
self.astral.elevation = self.elevation
self.astral.timezone = tz_name
# unsure why we need the location in 2.1
self._astral_location = _astral.LocationInfo(name='HABApp', )
self._astral_location.latitude = self.latitude
self._astral_location.longitude = self.longitude
self._astral_location.timezone = tz_name

self.astral_observer = self._astral_location.observer
self.astral_observer.elevation = self.elevation
2 changes: 1 addition & 1 deletion HABApp/core/const/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from .const import NotSet
from .const import MISSING
from . import topics
from .loop import loop
7 changes: 5 additions & 2 deletions HABApp/core/const/const.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
class NotSet:
class _MissingType:
def __repr__(self):
return '<NotSet>'
return '<Missing>'


MISSING = _MissingType()
28 changes: 27 additions & 1 deletion HABApp/openhab/http_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import typing

import aiohttp
import datetime
import ujson
from aiohttp.client import ClientResponse
from aiohttp_sse_client import client as sse_client
Expand All @@ -13,7 +14,6 @@
import HABApp.openhab.events
from ..config import Openhab as OpenhabConfig


log = logging.getLogger('HABApp.openhab.connection')
log_events = logging.getLogger('HABApp.EventBus.openhab')

Expand Down Expand Up @@ -373,3 +373,29 @@ async def async_remove_metadata(self, item_name: str, namespace: str):
fut = self.__session.delete(self.__get_openhab_url('rest/items/{:s}/metadata/{:s}', item_name, namespace))
ret = await self._check_http_response(fut)
return ret.status < 300

async def get_persistence_data(self,
item_name: str, persistence: typing.Optional[str],
start_time: typing.Optional[datetime.datetime],
end_time: typing.Optional[datetime.datetime]) -> dict:

params = {}
if persistence:
params['serviceId'] = persistence
if start_time is not None:
params['starttime'] = start_time.astimezone(None).strftime('%Y-%m-%dT%H:%M:%S.%f%z')
if end_time is not None:
params['endtime'] = end_time.astimezone(None).strftime('%Y-%m-%dT%H:%M:%S.%f%z')
if not params:
params = None

fut = self.__session.get(
self.__get_openhab_url('rest/persistence/items/{:s}', item_name),
params=params
)
ret = await self._check_http_response(fut)

if ret.status >= 300:
return {}
else:
return await ret.json(encoding='utf-8')
25 changes: 20 additions & 5 deletions HABApp/openhab/items/base_item.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import typing
import datetime

from HABApp.core.const import NotSet
from HABApp.core.const import MISSING
from HABApp.core.items.base_valueitem import BaseValueItem
from HABApp.openhab import get_openhab_interface

Expand All @@ -9,16 +10,30 @@ class OpenhabItem(BaseValueItem):
"""Base class for items which exists in OpenHAB.
"""

def oh_send_command(self, value: typing.Any = NotSet):
def oh_send_command(self, value: typing.Any = MISSING):
"""Send a command to the openHAB item
:param value: (optional) value to be sent. If not specified the item value will be used.
"""
get_openhab_interface().send_command(self.name, self.value if value is NotSet else value)
get_openhab_interface().send_command(self.name, self.value if value is MISSING else value)

def oh_post_update(self, value: typing.Any = NotSet):
def oh_post_update(self, value: typing.Any = MISSING):
"""Post an update to the openHAB item
:param value: (optional) value to be posted. If not specified the item value will be used.
"""
get_openhab_interface().post_update(self.name, self.value if value is NotSet else value)
get_openhab_interface().post_update(self.name, self.value if value is MISSING else value)

def get_persistence_data(self, persistence: typing.Optional[str] = None,
start_time: typing.Optional[datetime.datetime] = None,
end_time: typing.Optional[datetime.datetime] = None):
"""Query historical data from the OpenHAB persistence service
:param persistence: name of the persistence service (e.g. ``rrd4j``, ``mapdb``). If not set default will be used
:param start_time: return only items which are newer than this
:param end_time: return only items which are older than this
"""

return get_openhab_interface().get_persistence_data(
self.name, persistence, start_time, end_time
)
89 changes: 88 additions & 1 deletion HABApp/openhab/oh_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
import HABApp
import HABApp.core
import HABApp.openhab.events
from HABApp.util import log_exception
from HABApp.core.const import loop
from HABApp.core.items.base_valueitem import BaseValueItem
from HABApp.util import log_exception
from . import definitions
from .http_connection import HttpConnection

OPTIONAL_DT = typing.Optional[datetime.datetime]

log = logging.getLogger('HABApp.openhab.Connection')


Expand Down Expand Up @@ -69,6 +71,62 @@ def from_dict(cls, data) -> 'OpenhabItemDefinition':
return cls(**{k: v for k, v in data.items() if k in cls.__annotations__})


class OpenhabPersistenceData:

def __init__(self):
self.data: typing.Dict[float, typing.Union[int, float, str]] = {}

@classmethod
def from_dict(cls, data) -> 'OpenhabPersistenceData':
c = cls()
for entry in data['data']:
# calc as timestamp
time = entry['time'] / 1000
state = entry['state']
if '.' in state:
try:
state = float(state)
except ValueError:
pass
else:
try:
state = int(state)
except ValueError:
pass

c.data[time] = state
return c

def get_data(self, start_date: OPTIONAL_DT = None, end_date: OPTIONAL_DT = None):
if start_date is None and end_date is None:
return self.data

filter_start = start_date.timestamp() if start_date else None
filter_end = end_date.timestamp() if end_date else None

ret = {}
for ts, val in self.data.items():
if filter_start is not None and ts < filter_start:
continue
if filter_end is not None and ts > filter_end:
continue
ret[ts] = val
return ret

def min(self, start_date: OPTIONAL_DT = None, end_date: OPTIONAL_DT = None) -> typing.Optional[float]:
return min(self.get_data(start_date, end_date).values(), default=None)

def max(self, start_date: OPTIONAL_DT = None, end_date: OPTIONAL_DT = None) -> typing.Optional[float]:
return max(self.get_data(start_date, end_date).values(), default=None)

def average(self, start_date: OPTIONAL_DT = None, end_date: OPTIONAL_DT = None) -> typing.Optional[float]:
values = list(self.get_data(start_date, end_date).values())
ct = len(values)
if ct == 0:
return None
return sum(values) / ct


class OpenhabInterface:
def __init__(self, connection):
self.__connection: HttpConnection = connection
Expand Down Expand Up @@ -287,6 +345,35 @@ def remove_metadata(self, item_name: str, namespace: str):
)
return fut.result()

def get_persistence_data(self,
item_name: str, persistence: typing.Optional[str],
start_time: typing.Optional[datetime.datetime],
end_time: typing.Optional[datetime.datetime]) -> OpenhabPersistenceData:
"""Query historical data from the OpenHAB persistence service
:param item_name: name of the persistet item
:param persistence: name of the persistence service (e.g. ``rrd4j``, ``mapdb``). If not set default will be used
:param start_time: return only items which are newer than this
:param end_time: return only items which are older than this
"""
if not self.__connection.is_online:
return None

assert isinstance(item_name, str) and item_name, item_name
assert isinstance(persistence, str) or persistence is None, persistence
assert isinstance(start_time, datetime.datetime) or start_time is None, start_time
assert isinstance(end_time, datetime.datetime) or end_time is None, end_time

fut = asyncio.run_coroutine_threadsafe(
self.__connection.get_persistence_data(
item_name=item_name, persistence=persistence, start_time=start_time, end_time=end_time
),
loop
)

ret = fut.result()
return OpenhabPersistenceData.from_dict(ret)


OH_INTERFACE = None

Expand Down
10 changes: 6 additions & 4 deletions HABApp/rule/scheduler/sun_cb.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from datetime import timedelta, datetime
from datetime import datetime, timedelta

import astral.sun
from pytz import utc

import HABApp
Expand All @@ -21,9 +22,10 @@ def set_next_run_time(self, date_time: TYPING_DATE_TIME) -> 'ScheduledCallbackBa
raise NotImplementedError()

def _calculate_next_call(self):
func = getattr(HABApp.config.config.CONFIG.location.astral, self._method)
func = getattr(astral.sun, self._method)
observer = HABApp.config.CONFIG.location.astral_observer

dt = datetime.now().date()
self._next_base: datetime = func(date=dt, local=False)
self._next_base: datetime = func(observer=observer, date=dt)
if self._next_base < datetime.now(tz=utc):
self._next_base: datetime = func(date=dt + timedelta(days=1), local=False)
self._next_base: datetime = func(observer=observer, date=dt + timedelta(days=1))
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def load_version() -> str:
'paho-mqtt',
'ujson',
'watchdog',
'astral',
'astral>=2.1,<3',
'pytz',
'tzlocal',

Expand Down

0 comments on commit bb2f72b

Please sign in to comment.