Skip to content

Commit

Permalink
0.12.6 (#124)
Browse files Browse the repository at this point in the history
0.12.6
- HABApp finds the correct config if the command arg is the config.yml instead of the config folder
- Files and folders are loaded in alphabetical order
- Binary data from mqtt work now as expected
- removed 'auto_disable_on' from MultiModeItem
- Fixes issue where sun functions were triggered multiple times when using scheduler boundaries
  • Loading branch information
spacemanspiff2007 authored Apr 2, 2020
1 parent 46d47bd commit 949d2c2
Show file tree
Hide file tree
Showing 28 changed files with 178 additions and 140 deletions.
17 changes: 17 additions & 0 deletions .pyip.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# set the default branch
# default: empty, the default branch on GitHub
branch: Develop

# set a global prefix for PRs
# default: empty
pr_prefix: "PyUp"

# Specify requirement files by hand, default is empty
# default: empty
# allowed: list
requirements:
- _doc/requirements.txt:
update: False
- requirements.txt:
update: True
pin: True
4 changes: 4 additions & 0 deletions HABApp/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ def find_config_folder(arg_config_path: typing.Optional[Path]) -> Path:
if v_env:
check_path.append(Path(v_env) / 'HABApp') # Virtual env dir
else:
# in case the user specifies the config.yml we automatically switch to the parent folder
if arg_config_path.name.lower() == 'config.yml' and arg_config_path.is_file():
arg_config_path = arg_config_path.parent

# Override automatic config detection if something is specified through command line
check_path = [arg_config_path]

Expand Down
2 changes: 1 addition & 1 deletion HABApp/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__VERSION__ = '0.12.5'
__VERSION__ = '0.12.6'
5 changes: 4 additions & 1 deletion HABApp/core/event_bus_listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,7 @@ def cancel(self):

def desc(self):
# return description
return f'"{self.topic}" (type {self.event_filter})'
_type = str(self.event_filter)
if _type.startswith("<class '"):
_type = _type[8:-2]
return f'"{self.topic}" (type {_type})'
2 changes: 1 addition & 1 deletion HABApp/core/items/base_item_times.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import typing

from HABApp.core.wrapper import log_exception
from .base_item_watch import BaseWatch, ItemNoUpdateWatch, ItemNoChangeWatch
from .base_item_watch import BaseWatch, ItemNoChangeWatch, ItemNoUpdateWatch
from ..const import loop


Expand Down
16 changes: 10 additions & 6 deletions HABApp/mqtt/mqtt_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,16 +125,14 @@ def on_disconnect(self, client, userdata, rc):

@log_exception
def process_msg(self, client, userdata, message: mqtt.MQTTMessage):
topic = message.topic
topic: str = message.topic

try:
payload = message.payload.decode("utf-8")
except UnicodeDecodeError:
pass

if log_msg.isEnabledFor(logging.DEBUG):
log_msg._log(logging.DEBUG, f'{topic} ({message.qos}): {payload}', [])
if log_msg.isEnabledFor(logging.DEBUG):
log_msg._log(logging.DEBUG, f'{topic} ({message.qos}): {payload}', [])

if isinstance(topic, str):
# load json dict and list
if payload.startswith('{') and payload.endswith('}') or payload.startswith('[') and payload.endswith(']'):
try:
Expand All @@ -150,6 +148,12 @@ def process_msg(self, client, userdata, message: mqtt.MQTTMessage):
payload = float(payload)
except ValueError:
pass
except UnicodeDecodeError:
# Payload ist binary
payload = message.payload
if log_msg.isEnabledFor(logging.DEBUG):
log_msg._log(logging.DEBUG, f'{topic} ({message.qos}): {payload[:20]}...', [])


# get the mqtt item
_item = HABApp.mqtt.items.MqttItem.get_create_item(topic)
Expand Down
4 changes: 2 additions & 2 deletions HABApp/openhab/http_connection.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import asyncio
import datetime
import logging
import traceback
import typing

import aiohttp
import datetime
from aiohttp.client import ClientResponse
from aiohttp_sse_client import client as sse_client

import HABApp
import HABApp.core
import HABApp.openhab.events
from ..config import Openhab as OpenhabConfig
from ..core.const.json import load_json, dump_json
from ..core.const.json import dump_json, load_json

log = logging.getLogger('HABApp.openhab.connection')
log_events = logging.getLogger('HABApp.EventBus.openhab')
Expand Down
39 changes: 25 additions & 14 deletions HABApp/openhab/items/image_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,24 @@
from base64 import b64encode
from binascii import hexlify

from HABApp.openhab import get_openhab_interface
from HABApp.openhab.items.base_item import OpenhabItem
from ..definitions import RawValue


def _convert_bytes(data: bytes, img_type: typing.Optional[str]) -> str:
assert isinstance(data, bytes), type(data)

# try to automatically found out what kind of file we have
if img_type is None:
if data.startswith(b'\xFF\xD8\xFF'):
img_type = 'jpeg'
elif data.startswith(b'\x89\x50\x4E\x47'):
img_type = 'png'
assert img_type in ('jpeg', 'png'), f'Image type: "{img_type}", File Signature: {hexlify(data[:10])}'

return f'data:image/{img_type:s};base64,{b64encode(data).decode("ascii")}'


class ImageItem(OpenhabItem):
"""ImageItem which accepts and converts the data types from OpenHAB"""

Expand All @@ -30,22 +43,20 @@ def set_value(self, new_value) -> bool:
# bytes
return super().set_value(new_value.value)

def post_update(self, data: bytes, img_type: typing.Optional[str] = None):
def oh_post_update(self, data: bytes, img_type: typing.Optional[str] = None):
"""Post an update to an openhab image with new image data. Image type is automatically detected,
in rare cases when this does not work it can be set manually.
:param data: image data
:param img_type: (optional) what kind of image, ``jpeg`` or ``png``
"""
assert isinstance(data, bytes), type(data)
# try to automatically found out what kind of file we have
if img_type is None:
if data.startswith(b'\xFF\xD8\xFF'):
img_type = 'jpeg'
elif data.startswith(b'\x89\x50\x4E\x47'):
img_type = 'png'
assert img_type in ('jpeg', 'png'), f'Image type: "{img_type}", File Signature: {hexlify(data[:10])}'

state = f'data:image/{img_type};base64,{b64encode(data).decode("ascii")}'
get_openhab_interface().post_update(self.name, state)
return None
return super().oh_post_update(_convert_bytes(data, img_type))

def oh_send_command(self, data: bytes, img_type: typing.Optional[str] = None):
"""Send a command to an openhab image with new image data. Image type is automatically detected,
in rare cases when this does not work it can be set manually.
:param data: image data
:param img_type: (optional) what kind of image, ``jpeg`` or ``png``
"""
return super().oh_send_command(_convert_bytes(data, img_type))
2 changes: 1 addition & 1 deletion HABApp/openhab/map_items.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,4 @@ def map_items(name, openhab_type: str, openhab_value: str):
img.set_value(RawValue(value))
return img

raise ValueError(f'Unknown Openhab type: {openhab_type}')
raise ValueError(f'Unknown Openhab type: {openhab_type} for {name}')
3 changes: 3 additions & 0 deletions HABApp/parameters/parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ def value(self) -> typing.Any:
"""
return _get_value(self.filename, *self.keys)

def __bool__(self):
return bool(self.value)

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

Expand Down
1 change: 0 additions & 1 deletion HABApp/rule/rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,6 @@ def run_on_sun(self, sun_event: str, callback, *args, run_if_missed=False, **kwa
future_event = SunScheduledCallback(cb, *args, **kwargs)
future_event.sun_trigger(sun_event)
future_event._calculate_next_call()
future_event.update_run_time()
self.__future_events.append(future_event)
return future_event

Expand Down
17 changes: 7 additions & 10 deletions HABApp/rule/scheduler/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def set_next_run_time(self, date_time: TYPING_DATE_TIME) -> 'ScheduledCallbackBa
self._next_base = date_time

# Check boundaries
self.update_run_time()
self._update_run_time()
return self

def _calculate_next_call(self):
Expand All @@ -79,7 +79,7 @@ def earliest(self, time_obj: typing.Optional[time]) -> 'ScheduledCallbackBase':
changed = self._earliest != time_obj
self._earliest = time_obj
if changed:
self.update_run_time()
self._update_run_time()
return self

def latest(self, time_obj: typing.Optional[time]) -> 'ScheduledCallbackBase':
Expand All @@ -91,7 +91,7 @@ def latest(self, time_obj: typing.Optional[time]) -> 'ScheduledCallbackBase':
changed = self._latest != time_obj
self._latest = time_obj
if changed:
self.update_run_time()
self._update_run_time()
return self

def offset(self, timedelta_obj: typing.Optional[timedelta]) -> 'ScheduledCallbackBase':
Expand All @@ -103,7 +103,7 @@ def offset(self, timedelta_obj: typing.Optional[timedelta]) -> 'ScheduledCallbac
changed = self._offset != timedelta_obj
self._offset = timedelta_obj
if changed:
self.update_run_time()
self._update_run_time()
return self

def jitter(self, secs: typing.Optional[int]) -> 'ScheduledCallbackBase':
Expand All @@ -115,7 +115,7 @@ def jitter(self, secs: typing.Optional[int]) -> 'ScheduledCallbackBase':
changed = self._jitter != secs
self._jitter = secs
if changed:
self.update_run_time()
self._update_run_time()
return self

def boundary_func(self, func: typing.Optional[typing.Callable[[datetime], datetime]]):
Expand All @@ -126,12 +126,10 @@ def boundary_func(self, func: typing.Optional[typing.Callable[[datetime], dateti
changed = self._boundary_func != func
self._boundary_func = func
if changed:
self.update_run_time()
self._update_run_time()
return self

def update_run_time(self) -> 'ScheduledCallbackBase':
"""Update the next time the job will be run. Call this if some boundaries have changed"""

def _update_run_time(self) -> 'ScheduledCallbackBase':
# Starting point is always the next call
self._next_call = self._next_base

Expand Down Expand Up @@ -186,7 +184,6 @@ def execute(self) -> bool:

self.run_counter += 1
self._calculate_next_call()
self.update_run_time()
self._callback.run(*self._args, **self._kwargs)
return True

Expand Down
4 changes: 2 additions & 2 deletions HABApp/rule/scheduler/reoccurring_cb.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def __init__(self, callback, *args, **kwargs):

def _calculate_next_call(self):
self._next_base += self._interval
self.update_run_time()
self._update_run_time()

def interval(self, interval: typing.Union[int, timedelta]) -> 'ReoccurringScheduledCallback':
if isinstance(interval, int):
Expand Down Expand Up @@ -45,4 +45,4 @@ def _calculate_next_call(self):
while not loc.isoweekday() in self._weekdays:
self._next_base += timedelta(days=1)
loc = self._next_base.astimezone(local_tz)
self.update_run_time()
self._update_run_time()
9 changes: 6 additions & 3 deletions HABApp/rule/scheduler/sun_cb.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ def _calculate_next_call(self):
observer = HABApp.config.CONFIG.location.astral_observer

dt = datetime.now().date()
self._next_base: datetime = func(observer=observer, date=dt)
if self._next_base < datetime.now(tz=utc):
self._next_base: datetime = func(observer=observer, date=dt + timedelta(days=1))
self._next_base: datetime = func(observer=observer, date=dt).replace(microsecond=0)
self._update_run_time()

if self._next_call < datetime.now(tz=utc):
self._next_base: datetime = func(observer=observer, date=dt + timedelta(days=1)).replace(microsecond=0)
self._update_run_time()
5 changes: 3 additions & 2 deletions HABApp/runtime/folder_watcher/habappfileevent.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ def create_habapp_event(self, path: Path):

def trigger_load_for_all_files(self, delay: int = None):

# trigger event for every file
for f in self.folder.glob(f'**/*{self.file_ending}' if self.recursive else f'*{self.file_ending}'):
# trigger event for every file, we load in alphabetical order
rule_files = self.folder.glob(f'**/*{self.file_ending}' if self.recursive else f'*{self.file_ending}')
for f in sorted(rule_files, key=lambda x: x.relative_to(self.folder)):
if not f.name.endswith(self.file_ending):
continue

Expand Down
Loading

0 comments on commit 949d2c2

Please sign in to comment.