Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
spacemanspiff2007 committed Sep 18, 2023
1 parent 0e741b8 commit ff7be05
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 3 deletions.
6 changes: 5 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,11 @@ MyOpenhabRule()
```

# Changelog
#### 23.09.0 (2023-XX-XX)
#### 23.09.1 (2023-09-18)
- Log a warning for broken links between items and things
- Fix CI

#### 23.09.0 (2023-09-12)
- Switched version number scheme to CalVer (Calendar Versioning): ``YEAR.MONTH.COUNTER``
- Fail fast when a value instead of a callback is passed to the event listener / scheduler
- Completely removed types and type hints from traceback
Expand Down
2 changes: 1 addition & 1 deletion src/HABApp/__version__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@
# Development versions contain the DEV-COUNTER postfix:
# - 23.09.0.DEV-1

__version__ = '23.09.0'
__version__ = '23.09.1'
3 changes: 3 additions & 0 deletions src/HABApp/mqtt/connection/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ async def on_disconnected(self, connection: MqttConnection, context: CONTEXT_TYP
assert context is not None

connection.log.info('Disconnected')
# remove this check when https://github.com/sbtinstruments/aiomqtt/pull/249 gets merged
if not context._lock.locked():
await context._lock.acquire()
await context.__aexit__(None, None, None)


Expand Down
3 changes: 2 additions & 1 deletion src/HABApp/openhab/connection/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def setup():
from HABApp.openhab.connection.plugins import (WaitForStartlevelPlugin, LoadOpenhabItemsPlugin,
SseEventListenerPlugin, OUTGOING_PLUGIN, LoadTransformationsPlugin,
WaitForPersistenceRestore, PingPlugin, ThingOverviewPlugin,
TextualThingConfigPlugin)
TextualThingConfigPlugin, BrokenLinksPlugin)

connection = Connections.add(OpenhabConnection())
connection.register_plugin(CONNECTION_HANDLER)
Expand All @@ -59,6 +59,7 @@ def setup():
connection.register_plugin(WaitForPersistenceRestore(), 110)
connection.register_plugin(TextualThingConfigPlugin(), 120)
connection.register_plugin(ThingOverviewPlugin(), 500_000)
connection.register_plugin(BrokenLinksPlugin(), 500_001)

connection.register_plugin(ConnectionStateToEventBusPlugin())
connection.register_plugin(AutoReconnectPlugin())
Expand Down
1 change: 1 addition & 0 deletions src/HABApp/openhab/connection/plugins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
from .wait_for_restore import WaitForPersistenceRestore
from .overview_things import ThingOverviewPlugin
from .plugin_things import TextualThingConfigPlugin
from .overview_broken_links import BrokenLinksPlugin
51 changes: 51 additions & 0 deletions src/HABApp/openhab/connection/plugins/overview_broken_links.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from __future__ import annotations

import logging
from typing import Final

from HABApp.config import CONFIG
from HABApp.core.connections import BaseConnectionPlugin
from HABApp.core.internals import uses_item_registry
from HABApp.core.logger import log_warning
from HABApp.openhab.connection.connection import OpenhabConnection
from HABApp.openhab.connection.handler.func_async import async_get_things, async_get_links

PING_CONFIG: Final = CONFIG.openhab.ping

Items = uses_item_registry()


class BrokenLinksPlugin(BaseConnectionPlugin[OpenhabConnection]):

def __init__(self, name: str | None = None):
super().__init__(name)
self.do_run = True

async def on_online(self):
if not self.do_run:
return None
self.do_run = False

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

things = await async_get_things()
links = await async_get_links()

available_things = {t.uid for t in things}
available_channels = {c.uid for t in things for c in t.channels}

for link in sorted(links, key=lambda x: x.channel):
if not Items.item_exists(link.item):
log_warning(log, f'Item "{link.item}" does not exist! '
f'(link between item "{link.item:s}" and channel "{link.channel:s}")')
continue

if link.channel not in available_channels:
# check if the thing exists
thing_uid, channel_id = link.channel.rsplit(':', maxsplit=1)
if thing_uid in available_things:
log_warning(log, f'Channel "{channel_id}" on thing "{thing_uid:s}" does not exist! '
f'(link between item "{link.item:s}" and channel "{link.channel:s}")')
else:
log_warning(log, f'Thing "{thing_uid:s}" does not exist! '
f'(link between item "{link.item:s}" and channel "{link.channel:s}")')
60 changes: 60 additions & 0 deletions tests/test_openhab/test_plugins/test_broken_links.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from __future__ import annotations

import logging
from functools import partial

import HABApp.openhab.connection.plugins.overview_broken_links as plugin_module
from HABApp.core.internals import ItemRegistry
from HABApp.core.items import Item
from HABApp.openhab.definitions.rest import ThingResp, ItemChannelLinkResp
from HABApp.openhab.definitions.rest.things import ThingStatusResp, ChannelResp


async def _mock_things() -> list[ThingResp]:
return [
ThingResp(
uid='thing_type:uid', thing_type='thing_type',
status=ThingStatusResp(status='ONLINE', detail='ONLINE'),
editable=False,
channels=[
ChannelResp(uid='thing_type:uid:channel1', id='channel1', channel_type='channel1_type',
item_type='String', kind='STATE', linked_items=[]),
ChannelResp(uid='thing_type:uid:channel2', id='channel2', channel_type='channel2_type',
item_type='String', kind='STATE', linked_items=[])
]
)
]


async def _mock_links() -> list[ItemChannelLinkResp]:
return [
ItemChannelLinkResp(item='item1', channel='thing_type:uid:channel1', editable=True), # okay
ItemChannelLinkResp(item='item2', channel='thing_type:uid:channel1', editable=True), # item does not exist
ItemChannelLinkResp(item='item1', channel='thing_type:uid:channel3', editable=True), # channel does not exist
ItemChannelLinkResp(item='item1', channel='other_thing:uid:channel1', editable=True), # thing does not exist
]


async def test_link_warning(monkeypatch, ir: ItemRegistry, test_logs):
monkeypatch.setattr(plugin_module, 'async_get_things', _mock_things)
monkeypatch.setattr(plugin_module, 'async_get_links', _mock_links)

ir.add_item(Item('item1'))

p = plugin_module.BrokenLinksPlugin()
await p.on_online()

add = partial(test_logs.add_expected, 'HABApp.openhab.links', logging.WARNING)

add('Item "item2" does not exist! (link between item "item2" and channel "thing_type:uid:channel1")')
add('Channel "channel3" on thing "thing_type:uid" does not exist! '
'(link between item "item1" and channel "thing_type:uid:channel3")')
add('Thing "other_thing:uid" does not exist! (link between item "item1" and channel "other_thing:uid:channel1")')

# ensure that it runs only once
async def do_raise():
raise ValueError()

monkeypatch.setattr(plugin_module, 'async_get_things', do_raise)
monkeypatch.setattr(plugin_module, 'async_get_links', do_raise)
await p.on_online()

0 comments on commit ff7be05

Please sign in to comment.