Skip to content

Commit

Permalink
0.16.0
Browse files Browse the repository at this point in the history
- Support for OpenHAB 3
- bumped dependencies
  • Loading branch information
spacemanspiff2007 authored Oct 26, 2020
1 parent 2a4836a commit 26c4c61
Show file tree
Hide file tree
Showing 13 changed files with 281 additions and 49 deletions.
2 changes: 1 addition & 1 deletion HABApp/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.15.4'
__version__ = '0.16.0'
9 changes: 6 additions & 3 deletions HABApp/openhab/connection_handler/func_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@
ThingNotFoundError, ItemNotEditableError, ItemNotFoundError, MetadataNotEditableError
from HABApp.openhab.definitions.rest import ItemChannelLinkDefinition, LinkNotFoundError, OpenhabThingDefinition
from HABApp.openhab.definitions.rest.habapp_data import get_api_vals, load_habapp_meta
from .http_connection import delete, get, post, put, log
from .http_connection import delete, get, post, put, log, async_get_root, async_get_uuid


if typing.TYPE_CHECKING:
async_get_root = async_get_root
async_get_uuid = async_get_uuid


def convert_to_oh_type(_in: Any) -> str:
Expand All @@ -19,8 +24,6 @@ def convert_to_oh_type(_in: Any) -> str:
# 2018-11-19T09:47:38.284000+0100 -> 2018-11-19T09:47:38.284+0100
out = _in.astimezone(None).strftime('%Y-%m-%dT%H:%M:%S.%f%z')
return f'{out[:-8]}{out[-5:]}'
# elif isinstance(_in, HABApp.openhab.items.ColorItem):
# return f'{_in.hue:.1f},{_in.saturation:.1f},{_in.value:.1f}'
elif isinstance(_in, BaseValueItem):
return str(_in.value)
elif isinstance(_in, (set, list, tuple, frozenset)):
Expand Down
48 changes: 41 additions & 7 deletions HABApp/openhab/connection_handler/http_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
IS_ONLINE = False
IS_READ_ONLY = False

IS_OH2 = False

HTTP_PREFIX: Optional[str] = None

# HTTP options
Expand Down Expand Up @@ -221,6 +223,7 @@ async def start_connection():
HABApp.CONFIG.openhab.connection.password
)

# todo: add possibility to configure line size with read_bufsize
HTTP_SESSION = aiohttp.ClientSession(
timeout=aiohttp.ClientTimeout(total=None),
json_serialize=dump_json,
Expand All @@ -235,14 +238,20 @@ async def start_sse_event_listener():
# cache so we don't have to look up every event
call = ON_SSE_EVENT

options = {}
if HABApp.CONFIG.openhab.connection.user or HABApp.CONFIG.openhab.connection.password:
options['with_credentials'] = True

event_prefix = 'openhab' if not IS_OH2 else 'smarthome'

async with sse_client.EventSource(
url=
f'{HTTP_PREFIX}/rest/'
"events?topics=smarthome/items/," # Item updates
"smarthome/channels/," # Channel update
"smarthome/things/*/status," # Thing status updates
"smarthome/things/*/statuschanged" # Thing status changes
url=f'{HTTP_PREFIX}/rest/events?topics='
f'{event_prefix}/items/,' # Item updates
f'{event_prefix}/channels/,' # Channel update
f'{event_prefix}/things/*/status,' # Thing status updates
f'{event_prefix}/things/*/statuschanged' # Thing status changes
,
option=options,
session=HTTP_SESSION
) as event_source:
async for event in event_source:
Expand Down Expand Up @@ -283,15 +292,34 @@ async def async_get_uuid() -> str:
return await resp.text(encoding='utf-8')


async def async_get_root() -> dict:
resp = await get('', log_404=False)
if resp.status == 404:
return {}
return await resp.json(encoding='utf-8')


def patch_for_oh2(reverse=False):
global IS_OH2

IS_OH2 = True

# events are named different
HABApp.openhab.events.item_events.NAME_START = 16 if not reverse else 14
HABApp.openhab.events.thing_events.NAME_START = 17 if not reverse else 15
HABApp.openhab.events.channel_events.NAME_START = 19 if not reverse else 17


async def try_uuid():
global FUT_UUID, IS_ONLINE, FUT_SSE
global FUT_UUID, FUT_SSE, IS_ONLINE

# sleep before reconnect
await CONNECT_WAIT.wait()

log.debug('Trying to connect to OpenHAB ...')
try:
uuid = await async_get_uuid()
root = await async_get_root() # this will only work on OH3
except Exception as e:
if isinstance(e, (OpenhabDisconnectedError, OpenhabNotReadyYet)):
log.info('... offline!')
Expand All @@ -306,6 +334,12 @@ async def try_uuid():
else:
log.info(f'Connected to OpenHAB instance {uuid}')

info = root.get('runtimeInfo')
if info is None:
patch_for_oh2()
else:
log.info(f'OpenHAB version {info["version"]} ({info["buildString"]})')

IS_ONLINE = True

# start sse processing
Expand Down
4 changes: 2 additions & 2 deletions HABApp/openhab/connection_logic/plugin_things/items_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,15 @@ def create_items_file(path: Path, items_dict: Dict[str, UserItem]):
}

grouped_items = {None: []}
for _name, _item in sorted(items_dict.items()):
for _name, _item in items_dict.items():
m = RE_GROUP_NAMES.match(_name)
grp = grouped_items.setdefault(m.group(1) if m is not None else None, [])
grp.append(_get_item_val_dict(field_fmt, _item))

# aggregate single entry items to a block
_aggr = []
for _name, _items in grouped_items.items():
if len(_items) <= 1:
if len(_items) <= 1 and _name is not None:
_aggr.append(_name)
for _name in _aggr:
grouped_items[None].extend(grouped_items[_name])
Expand Down
7 changes: 6 additions & 1 deletion HABApp/openhab/events/channel_events.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
from .base_event import OpenhabEvent

# smarthome/channels/NAME/triggered -> 19
# openhab/channels/NAME/triggered -> 17
# todo: revert this once we go OH3 only
NAME_START: int = 17


class ChannelTriggeredEvent(OpenhabEvent):
def __init__(self, name: str = '', event: str = '', channel: str = ''):
Expand All @@ -11,7 +16,7 @@ def __init__(self, name: str = '', event: str = '', channel: str = ''):

@classmethod
def from_dict(cls, topic: str, payload: dict):
return cls(topic[19:-10], payload['event'], payload['channel'])
return cls(topic[NAME_START:-10], payload['event'], payload['channel'])

def __repr__(self):
return f'<{self.__class__.__name__} name: {self.name}, event: {self.event}>'
17 changes: 11 additions & 6 deletions HABApp/openhab/events/item_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
from ..map_values import map_openhab_values
from .base_event import OpenhabEvent

# smarthome/items/NAME/state -> 16
# openhab/items/NAME/state -> 14
# todo: revert this once we go OH3 only
NAME_START: int = 14


class ItemStateEvent(OpenhabEvent, HABApp.core.events.ValueUpdateEvent):
def __init__(self, name: str = '', value: typing.Any = None):
Expand All @@ -16,7 +21,7 @@ def __init__(self, name: str = '', value: typing.Any = None):
@classmethod
def from_dict(cls, topic: str, payload: dict):
# smarthome/items/NAME/state
return cls(topic[16:-6], map_openhab_values(payload['type'], payload['value']))
return cls(topic[NAME_START:-6], map_openhab_values(payload['type'], payload['value']))

def __repr__(self):
return f'<{self.__class__.__name__} name: {self.name}, value: {self.value}>'
Expand All @@ -34,7 +39,7 @@ def __init__(self, name: str = '', value: typing.Any = None, old_value: typing.A
def from_dict(cls, topic: str, payload: dict):
# smarthome/items/Ping/statechanged
return cls(
topic[16:-13],
topic[NAME_START:-13],
map_openhab_values(payload['type'], payload['value']),
map_openhab_values(payload['oldType'], payload['oldValue'])
)
Expand All @@ -53,7 +58,7 @@ def __init__(self, name: str = '', value: typing.Any = None):
@classmethod
def from_dict(cls, topic: str, payload: dict):
# smarthome/items/NAME/command
return cls(topic[16:-8], map_openhab_values(payload['type'], payload['value']))
return cls(topic[NAME_START:-8], map_openhab_values(payload['type'], payload['value']))

def __repr__(self):
return f'<{self.__class__.__name__} name: {self.name}, value: {self.value}>'
Expand Down Expand Up @@ -90,7 +95,7 @@ def from_dict(cls, topic: str, payload: dict):
# 'payload': '[{"type":"Switch","name":"Test","tags":[],"groupNames":[]},
# {"type":"Contact","name":"Test","tags":[],"groupNames":[]}]',
# 'type': 'ItemUpdatedEvent'
return cls(topic[16:-8], payload[0]['type'])
return cls(topic[NAME_START:-8], payload[0]['type'])

def __repr__(self):
return f'<{self.__class__.__name__} name: {self.name}, type: {self.type}>'
Expand All @@ -105,7 +110,7 @@ def __init__(self, name: str = ''):
@classmethod
def from_dict(cls, topic: str, payload: dict):
# smarthome/items/Test/removed
return cls(topic[16:-8])
return cls(topic[NAME_START:-8])

def __repr__(self):
return f'<{self.__class__.__name__} name: {self.name}>'
Expand All @@ -122,7 +127,7 @@ def __init__(self, name: str = '', value: typing.Any = None):
@classmethod
def from_dict(cls, topic: str, payload: dict):
# 'smarthome/items/NAME/statepredicted'
return cls(topic[16:-15], map_openhab_values(payload['predictedType'], payload['predictedValue']))
return cls(topic[NAME_START:-15], map_openhab_values(payload['predictedType'], payload['predictedValue']))

def __repr__(self):
return f'<{self.__class__.__name__} name: {self.name}, value: {self.value}>'
Expand Down
11 changes: 8 additions & 3 deletions HABApp/openhab/events/thing_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

from .base_event import OpenhabEvent

# smarthome/things/NAME/state -> 17
# openhab/things/NAME/state -> 15
# todo: revert this once we go OH3 only
NAME_START: int = 15


class ThingStatusInfoEvent(OpenhabEvent):
def __init__(self, name: str = '', status: str = '', detail: str = ''):
Expand All @@ -14,7 +19,7 @@ def __init__(self, name: str = '', status: str = '', detail: str = ''):
@classmethod
def from_dict(cls, topic: str, payload: dict):
# smarthome/things/chromecast:chromecast:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/status
return cls(name=topic[17:-7], status=payload['status'], detail=payload['statusDetail'])
return cls(name=topic[NAME_START:-7], status=payload['status'], detail=payload['statusDetail'])

def __repr__(self):
return f'<{self.__class__.__name__} name: {self.name}, status: {self.status}, detail: {self.detail}>'
Expand All @@ -33,7 +38,7 @@ def __init__(self, name: str = '', status: str = '', detail: str = '', old_statu
@classmethod
def from_dict(cls, topic: str, payload: dict):
# smarthome/things/chromecast:chromecast:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/statuschanged
name = topic[17:-14]
name = topic[NAME_START:-14]
new, old = payload
return cls(
name=name, status=new['status'], detail=new['statusDetail'],
Expand All @@ -56,7 +61,7 @@ def __init__(self, name: str = '', messages: typing.List[typing.Dict[str, str]]
@classmethod
def from_dict(cls, topic: str, payload: dict):
# 'smarthome/things/zwave:device:controller:my_node/config/status'
return cls(name=topic[17:-14], messages=payload['configStatusMessages'])
return cls(name=topic[NAME_START:-14], messages=payload['configStatusMessages'])

def __repr__(self):
return f'<{self.__class__.__name__} name: {self.name}, messages: {self.messages}>'
2 changes: 1 addition & 1 deletion conf_testing/lib/HABAppTests/openhab_tmp_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def __init__(self, item_name, item_type):
if self.item_name is None:
self.item_name = get_random_name()

def __enter__(self) -> HABApp.core.items.Item:
def __enter__(self) -> HABApp.openhab.items.OpenhabItem:
interface = HABApp.openhab.interface

if not interface.item_exists(self.item_name):
Expand Down
1 change: 1 addition & 0 deletions conf_testing/rules/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def test_sw_mode(self):

switch.off()
waiter.wait_for_state('OFF')
assert switch.is_off()
assert mode.enabled is False, mode.enabled

mode.set_value('asdf')
Expand Down
8 changes: 3 additions & 5 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
aiohttp-sse-client==0.1.7
aiohttp==3.6.2
aiohttp-sse-client==0.2.0
aiohttp==3.7.1
astral==2.2
bidict==0.21.2
easyco==0.2.2
Expand All @@ -10,9 +10,7 @@ stackprinter==0.2.4
tzlocal==2.1
voluptuous==0.12.0
watchdog==0.10.3

# This is optional
ujson==3.2.0
ujson==4.0.1

# Backports
dataclasses==0.7;python_version<"3.7"
Loading

0 comments on commit 26c4c61

Please sign in to comment.