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
async def unload(self): try: await self.__class__.UNLOAD_FUNC(self.name, self.path) except Exception as e: if not isinstance(e, AlreadyHandledFileError): process_exception(self.__class__.UNLOAD_FUNC, e, logger=self.LOGGER) self.set_state(FileState.FAILED) return None self.set_state(FileState.REMOVED) return None
async def load(self): assert self.state is FileState.DEPENDENCIES_OK, self.state try: await self.__class__.LOAD_FUNC(self.name, self.path) except Exception as e: if not isinstance(e, AlreadyHandledFileError): process_exception(self.__class__.LOAD_FUNC, e, logger=self.LOGGER) self.set_state(FileState.FAILED) return None self.set_state(FileState.LOADED) return None
def get_msg_payload(msg: MQTTMessage) -> Tuple[Optional[str], Any]: try: topic = msg._topic.decode('utf-8') raw = msg.payload try: val = raw.decode("utf-8") except UnicodeDecodeError: # Payload ist a byte stream if log.isEnabledFor(logging.DEBUG): log._log(logging.DEBUG, f'{topic} ({msg.qos}): {raw[:20]}...', []) return topic, raw if log.isEnabledFor(logging.DEBUG): log._log(logging.DEBUG, f'{topic} ({msg.qos}): {val}', []) # None if val == 'none' or val == 'None': return topic, None # bool if val == 'true' or val == 'True': return topic, True if val == 'false' or val == 'False': return topic, False # int if val.isdecimal(): return topic, int(val) # json list/dict if val.startswith('{') and val.endswith('}') or val.startswith('[') and val.endswith(']'): try: return topic, load_json(val) except ValueError: return topic, val # float or str try: return topic, float(val) except ValueError: return topic, val except Exception as e: process_exception('get_msg_payload', e, logger=log) return None, None
async def start(self, config_folder: Path): try: token = async_context.set('HABApp startup') # setup exception handler for the scheduler eascheduler.set_exception_handler(lambda x: process_exception('HABApp.scheduler', x)) # Start Folder watcher! HABApp.core.files.watcher.start() self.config_loader = HABApp.config.HABAppConfigLoader(config_folder) await HABApp.core.files.setup() # generic HTTP await HABApp.rule.interfaces._http.create_client() # openhab openhab_connection.setup() # Parameter Files await HABApp.parameters.parameter_files.setup_param_files() # Rule engine self.rule_manager = HABApp.rule_manager.RuleManager(self) await self.rule_manager.setup() # MQTT HABApp.mqtt.mqtt_connection.setup() HABApp.mqtt.mqtt_connection.connect() await openhab_connection.start() shutdown.register_func(HABApp.core.const.loop.stop, msg='Stopping asyncio loop') async_context.reset(token) except asyncio.CancelledError: pass except Exception as e: process_exception('Runtime.start', e) await asyncio.sleep(1) # Sleep so we can do a graceful shutdown shutdown.request_shutdown()
def map_item(name, openhab_type: str, openhab_value: str) -> typing.Optional[BaseItem]: try: assert isinstance(openhab_type, str), type(openhab_type) assert isinstance(openhab_value, str), type(openhab_value) value: typing.Optional[str] = openhab_value if openhab_value == 'NULL' or openhab_value == 'UNDEF': value = None # Quantity types are like this: Number:Temperature and have a unit set: "12.3 °C". # We have to remove the dimension from the type and remove the unit from the value if ':' in openhab_type: openhab_type, dimension = openhab_type.split(':') # if the item is not initialized its None and has no dimension if value is not None: value, _ = QuantityValue.split_unit(value) # Specific classes if openhab_type == "Switch": return SwitchItem(name, value) if openhab_type == "String": return StringItem(name, value) if openhab_type == "Contact": return ContactItem(name, value) if openhab_type == "Rollershutter": if value is None: return RollershutterItem(name, value) return RollershutterItem(name, float(value)) if openhab_type == "Dimmer": if value is None: return DimmerItem(name, value) return DimmerItem(name, float(value)) if openhab_type == "Number": if value is None: return NumberItem(name, value) # Number items can be int or float try: return NumberItem(name, int(value)) except ValueError: return NumberItem(name, float(value)) if openhab_type == "DateTime": if value is None: return DatetimeItem(name, value) dt = datetime.datetime.strptime(value.replace('+', '000+'), '%Y-%m-%dT%H:%M:%S.%f%z') # all datetimes from openhab have a timezone set so we can't easily compare them # --> TypeError: can't compare offset-naive and offset-aware datetimes dt = dt.astimezone( tz=None) # Changes datetime object so it uses system timezone dt = dt.replace(tzinfo=None) # Removes timezone awareness return DatetimeItem(name, dt) if openhab_type == "Color": if value is None: return ColorItem(name) return ColorItem(name, *(float(k) for k in value.split(','))) if openhab_type == "Image": img = ImageItem(name) if value is None: return img img.set_value(RawValue(value)) return img if openhab_type == "Group": return GroupItem(name, value) if openhab_type == "Location": return LocationItem(name, value) if openhab_type == "Player": return PlayerItem(name, value) raise ValueError(f'Unknown Openhab type: {openhab_type} for {name}') except Exception as e: process_exception('map_items', e, logger=log) return None
def map_item(name: str, type: str, value: Optional[str], tags: FrozenSet[str], groups: FrozenSet[str], metadata: Optional[Dict[str, Dict[str, Any]]]) -> \ Optional['HABApp.openhab.items.OpenhabItem']: try: assert isinstance(type, str) assert value is None or isinstance(value, str) if value == 'NULL' or value == 'UNDEF': value = None # map Metadata if metadata is not None: meta = Map({k: MetaData(v['value'], Map(v.get('config', {}))) for k, v in metadata.items()}) else: meta = Map() # Quantity types are like this: Number:Temperature and have a unit set: "12.3 °C". # We have to remove the dimension from the type and remove the unit from the value if ':' in type: type, dimension = type.split(':') # if the item is not initialized its None and has no dimension if value is not None: value, _ = QuantityValue.split_unit(value) # Specific classes if type == "Switch": return SwitchItem(name, value, tags=tags, groups=groups, metadata=meta) if type == "String": return StringItem(name, value, tags=tags, groups=groups, metadata=meta) if type == "Contact": return ContactItem(name, value, tags=tags, groups=groups, metadata=meta) if type == "Rollershutter": if value is None: return RollershutterItem(name, value, tags=tags, groups=groups, metadata=meta) return RollershutterItem(name, float(value), tags=tags, groups=groups, metadata=meta) if type == "Dimmer": if value is None: return DimmerItem(name, value, tags=tags, groups=groups, metadata=meta) return DimmerItem(name, float(value), tags=tags, groups=groups, metadata=meta) if type == "Number": if value is None: return NumberItem(name, value, tags=tags, groups=groups, metadata=meta) # Number items can be int or float try: return NumberItem(name, int(value), tags=tags, groups=groups, metadata=meta) except ValueError: return NumberItem(name, float(value), tags=tags, groups=groups, metadata=meta) if type == "DateTime": if value is None: return DatetimeItem(name, value, tags=tags, groups=groups, metadata=meta) # Todo: remove this once we go >= OH3.1 # Previous OH versions used a datetime string like this: # 2018-11-19T09:47:38.284+0100 # OH 3.1 uses # 2021-04-10T22:00:43.043996+0200 if len(value) == 28: value = value.replace('+', '000+') dt = datetime.datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%f%z') # all datetimes from openhab have a timezone set so we can't easily compare them # --> TypeError: can't compare offset-naive and offset-aware datetimes dt = dt.astimezone(tz=None) # Changes datetime object so it uses system timezone dt = dt.replace(tzinfo=None) # Removes timezone awareness return DatetimeItem(name, dt, tags=tags, groups=groups, metadata=meta) if type == "Color": if value is None: return ColorItem(name, tags=tags, groups=groups, metadata=meta) return ColorItem(name, *(float(k) for k in value.split(',')), tags=tags, groups=groups, metadata=meta) if type == "Image": img = ImageItem(name, tags=tags, groups=groups, metadata=meta) if value is None: return img img.set_value(RawValue(value)) return img if type == "Group": return GroupItem(name, value, tags=tags, groups=groups, metadata=meta) if type == "Location": return LocationItem(name, value, tags=tags, groups=groups, metadata=meta) if type == "Player": return PlayerItem(name, value, tags=tags, groups=groups, metadata=meta) raise ValueError(f'Unknown Openhab type: {type} for {name}') except Exception as e: process_exception('map_items', e, logger=log) return None