async def on_connect_function(self): data = await async_get_items(disconnect_on_error=True, all_metadata=True) if data is None: return None fresh_item_sync() found_items = len(data) for _dict in data: item_name = _dict['name'] new_item = map_item( item_name, _dict['type'], _dict['state'], frozenset(_dict['tags']), frozenset(_dict['groupNames']), _dict.get('metadata', {})) # type: HABApp.openhab.items.OpenhabItem if new_item is None: continue add_to_registry(new_item, True) # remove items which are no longer available ist = set(Items.get_all_item_names()) soll = {k['name'] for k in data} for k in ist - soll: if isinstance(Items.get_item(k), HABApp.openhab.items.OpenhabItem): Items.pop_item(k) log.info(f'Updated {found_items:d} Items') # try to update things, too data = await async_get_things(disconnect_on_error=True) if data is None: return None Thing = HABApp.openhab.items.Thing for t_dict in data: name = t_dict['UID'] try: thing = Items.get_item(name) if not isinstance(thing, Thing): log.warning( f'Item {name} has the wrong type ({type(thing)}), expected Thing' ) thing = Thing(name) except Items.ItemNotFoundException: thing = Thing(name) thing.status = t_dict['statusInfo']['status'] Items.add_item(thing) # remove things which were deleted ist = set(Items.get_all_item_names()) soll = {k['UID'] for k in data} for k in ist - soll: if isinstance(Items.get_item(k), Thing): Items.pop_item(k) log.info(f'Updated {len(data):d} Things') return None
def on_sse_event(event_dict: dict): try: # Lookup corresponding OpenHAB event event = get_event(event_dict) # Update item in registry BEFORE posting to the event bus # so the items have the correct state when we process the event in a rule try: if isinstance(event, ValueUpdateEvent): __item = Items.get_item( event.name ) # type: HABApp.core.items.base_item.BaseValueItem __item.set_value(event.value) EventBus.post_event(event.name, event) return None if isinstance(event, ThingStatusInfoEvent): __thing = Items.get_item( event.name) # type: HABApp.openhab.items.Thing __thing.process_event(event) EventBus.post_event(event.name, event) return None # Workaround because there is no GroupItemStateEvent if isinstance(event, GroupItemStateChangedEvent): __item = Items.get_item( event.name) # type: HABApp.openhab.items.GroupItem __item.set_value(event.value) EventBus.post_event(event.name, event) return None except ItemNotFoundException: log_warning( log, f'Received {event.__class__.__name__} for {event.name} but item does not exist!' ) # Post the event anyway EventBus.post_event(event.name, event) return None if isinstance(event, ItemRemovedEvent): remove_from_registry(event.name) EventBus.post_event(event.name, event) return None # These events require that we query openhab because of the metadata so we have to do it in a task # They also change the item registry if isinstance(event, (ItemAddedEvent, ItemUpdatedEvent)): create_task(item_event(event)) return None HABApp.core.EventBus.post_event(event.name, event) except Exception as e: process_exception(func=on_sse_event, e=e) return None
def on_sse_event(event_dict: dict): # Lookup corresponding OpenHAB event event = get_event(event_dict) # Update item in registry BEFORE posting to the event bus # so the items have the correct state when we process the event in a rule try: if isinstance(event, HABApp.core.events.ValueUpdateEvent): __item = Items.get_item( event.name) # type: HABApp.core.items.base_item.BaseValueItem __item.set_value(event.value) HABApp.core.EventBus.post_event(event.name, event) return None elif isinstance(event, HABApp.openhab.events.ThingStatusInfoEvent): __thing = Items.get_item( event.name) # type: HABApp.openhab.items.Thing __thing.process_event(event) HABApp.core.EventBus.post_event(event.name, event) return None except HABApp.core.Items.ItemNotFoundException: pass # Events which change the ItemRegistry if isinstance(event, (HABApp.openhab.events.ItemAddedEvent, HABApp.openhab.events.ItemUpdatedEvent)): item = map_item(event.name, event.type, 'NULL') if item is None: return None # check already existing item so we can print a warning if something changes try: existing_item = Items.get_item(item.name) if isinstance(existing_item, item.__class__): # it's the same item class so we don't replace it! item = existing_item else: log.warning( f'Item changed type from {existing_item.__class__} to {item.__class__}' ) except Items.ItemNotFoundException: pass # always overwrite with new definition Items.set_item(item) elif isinstance(event, HABApp.openhab.events.ItemRemovedEvent): Items.pop_item(event.name) # Send Event to Event Bus HABApp.core.EventBus.post_event(event.name, event) return None
def get_create_item(cls, name: str, write_topic: Optional[str] = None, initial_value=None) -> 'MqttPairItem': """Creates a new item in HABApp and returns it or returns the already existing one with the given name. HABApp tries to automatically derive the write topic from the item name. In cases where this does not work it can be specified manually. :param name: item name (topic that reports the state) :param write_topic: topic that is used to write values or ``None`` (default) to build it automatically :param initial_value: state the item will have if it gets created :return: item """ assert isinstance(name, str), type(name) # try to build write topic if write_topic is None: write_topic = build_write_topic(name) try: item = Items.get_item(name) except Items.ItemNotFoundException: item = cls(name, write_topic=write_topic, initial_value=initial_value) Items.add_item(item) assert isinstance(item, cls), f'{cls} != {type(item)}' return item
def process_msg(client, userdata, message: mqtt.MQTTMessage): topic, payload = get_msg_payload(message) if topic is None: return None _item = None # type: typing.Optional[HABApp.mqtt.items.MqttBaseItem] try: _item = Items.get_item(topic) # type: HABApp.mqtt.items.MqttBaseItem except HABApp.core.Items.ItemNotFoundException: # only create items for if the message has the retain flag if message.retain: _item = Items.create_item( topic, HABApp.mqtt.items.MqttItem) # type: HABApp.mqtt.items.MqttItem # we don't have an item -> we process only the event if _item is None: HABApp.core.EventBus.post_event(topic, MqttValueUpdateEvent(topic, payload)) return None # Remember state and update item before doing callbacks _old_state = _item.value _item.set_value(payload) # Post events HABApp.core.EventBus.post_event(topic, MqttValueUpdateEvent(topic, payload)) if _old_state != payload: HABApp.core.EventBus.post_event( topic, MqttValueChangeEvent(topic, payload, _old_state))
def add_to_registry(item: 'HABApp.openhab.items.OpenhabItem', set_value=False): name = item.name for grp in item.groups: MEMBERS.setdefault(grp, set()).add(name) if not Items.item_exists(name): return Items.add_item(item) existing = Items.get_item(name) if isinstance(existing, item.__class__): # If we load directly through the API and not through an event we have to set the value if set_value: existing.set_value(item.value) # remove old groups for grp in set(existing.groups) - set(item.groups): MEMBERS.get(grp, set()).discard(name) # same type - it was only an item update (e.g. label)! existing.tags = item.tags existing.groups = item.groups return None log_warning( log, f'Item type changed from {existing.__class__} to {item.__class__}') # Replace existing item with the updated definition Items.pop_item(name) Items.add_item(item)
def check(self, now): if self.is_canceled: return None try: item = Items.get_item(self.name) except Items.ItemNotFoundException: return None timestamp = item._last_change if self.__watch_only_changes else item._last_update duration = now - timestamp if duration < self.duration_const: self.executed = False return None if self.executed: return None EventBus.post_event( self.name, (ValueNoChangeEvent if self.__watch_only_changes else ValueNoUpdateEvent)( self.name, item.value, int(duration.total_seconds()) ) ) self.executed = True
def test_retain_create(): topic = '/test/creation' assert not Items.item_exists(topic) process_msg(None, None, MqttDummyMsg(topic, 'aaa', retain=False)) assert not Items.item_exists(topic) # Retain True will create the item process_msg(None, None, MqttDummyMsg(topic, 'adsf123', retain=True)) assert Items.item_exists(topic) assert Items.get_item(topic).value == 'adsf123' Items.pop_item(topic)
def remove_from_registry(name: str): if not Items.item_exists(name): return None item = Items.get_item(name) # type: HABApp.openhab.items.OpenhabItem for grp in item.groups: MEMBERS.get(grp, set()).discard(name) if isinstance(item, HABApp.openhab.items.GroupItem): MEMBERS.pop(name, None) Items.pop_item(name) return None
def test_item(self): NAME = 'test' created_item = Item(NAME) Items.add_item(created_item) self.assertTrue(Items.item_exists(NAME)) self.assertIs(created_item, Items.get_item(NAME)) self.assertEqual(Items.get_all_item_names(), [NAME]) self.assertEqual(Items.get_all_items(), [created_item]) self.assertIs(created_item, Items.pop_item(NAME)) self.assertEqual(Items.get_all_items(), [])
def get_create_item(cls, name: str, initial_value=None) -> 'MqttItem': """Creates a new item in HABApp and returns it or returns the already existing one with the given name :param name: item name :param initial_value: state the item will have if it gets created :return: item """ assert isinstance(name, str), type(name) try: item = Items.get_item(name) except Items.ItemNotFoundException: item = cls(name, initial_value) Items.add_item(item) assert isinstance(item, cls), f'{cls} != {type(item)}' return item
async def on_connect_function(self): data = await async_get_items() if data is None: return None found_items = len(data) for _dict in data: item_name = _dict['name'] new_item = map_item(item_name, _dict['type'], _dict['state']) if new_item is None: continue try: # if the item already exists and it has the correct type just update its state # Since we load the items before we load the rules this should actually never happen existing_item = Items.get_item( item_name) # type: HABApp.core.items.BaseValueItem if isinstance(existing_item, new_item.__class__): existing_item.set_value( new_item.value ) # use the converted state from the new item here new_item = existing_item except Items.ItemNotFoundException: pass # create new item or change item type Items.set_item(new_item) # remove items which are no longer available ist = set(Items.get_all_item_names()) soll = {k['name'] for k in data} for k in ist - soll: if isinstance(Items.get_item(k), HABApp.openhab.items.OpenhabItem): Items.pop_item(k) log.info(f'Updated {found_items:d} Items') # try to update things, too data = await async_get_things() if data is None: return None Thing = HABApp.openhab.items.Thing for t_dict in data: name = t_dict['UID'] try: thing = HABApp.core.Items.get_item(name) if not isinstance(thing, Thing): log.warning( f'Item {name} has the wrong type ({type(thing)}), expected Thing' ) thing = Thing(name) except HABApp.core.Items.ItemNotFoundException: thing = Thing(name) thing.status = t_dict['statusInfo']['status'] HABApp.core.Items.set_item(thing) # remove things which were deleted ist = set(HABApp.core.Items.get_all_item_names()) soll = {k['UID'] for k in data} for k in ist - soll: if isinstance(HABApp.core.Items.get_item(k), Thing): HABApp.core.Items.pop_item(k) log.info(f'Updated {len(data):d} Things') return None
def get_members( group_name: str) -> Tuple['HABApp.openhab.items.OpenhabItem', ...]: ret = [] for name in MEMBERS.get(group_name, []): ret.append(Items.get_item(name)) return tuple(sorted(ret))