def request_file_unload(self, name: str, path: Path, request_lock=True): path_str = str(path) try: if request_lock: self.__load_lock.acquire() # Only unload already loaded files with self.__files_lock: already_loaded = path_str in self.files if not already_loaded: log_warning( log, f'Rule file {path} is not yet loaded and therefore can not be unloaded' ) return None log.debug(f'Removing file: {path}') with self.__files_lock: rule = self.files.pop(path_str) rule.unload() except Exception as e: err = HABApp.core.logger.HABAppError(log) err.add(f"Could not remove {path}!") err.add_exception(e, True) err.dump() return None finally: if request_lock: self.__load_lock.release()
async def request_file_load(self, name: str, path: Path): path_str = str(path) # Only load existing files if not path.is_file(): log_warning( log, f'Rule file {name} ({path}) does not exist and can not be loaded!' ) return None with self.__load_lock: # Unload if we have already loaded with self.__files_lock: already_loaded = path_str in self.files if already_loaded: await self.request_file_unload(name, path, request_lock=False) log.debug(f'Loading file: {name}') with self.__files_lock: self.files[path_str] = file = RuleFile(self, name, path) ok = await HABApp.core.const.loop.run_in_executor( HABApp.core.WrappedFunction._WORKERS, file.load) if not ok: self.files.pop(path_str) log.warning(f'Failed to load {path_str}!') raise AlreadyHandledFileError() log.debug(f'File {name} successfully loaded!') # Do simple checks which prevent errors file.check_all_rules()
async def request_file_unload(self, name: str, path: Path, request_lock=True): path_str = str(path) try: if request_lock: self.__load_lock.acquire() # Only unload already loaded files with self.__files_lock: already_loaded = path_str in self.files if not already_loaded: log_warning( log, f'Rule file {path} is not yet loaded and therefore can not be unloaded' ) return None log.debug(f'Removing file: {name}') with self.__files_lock: rule = self.files.pop(path_str) await HABApp.core.const.loop.run_in_executor( HABApp.core.WrappedFunction._WORKERS, rule.unload) finally: if request_lock: self.__load_lock.release()
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 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 request_file_load(self, name: str, path: Path): path_str = str(path) # Only load existing files if not path.is_file(): log_warning( log, f'Rule file {path} does not exist and can not be loaded!') return None with self.__load_lock: # Unload if we have already loaded with self.__files_lock: already_loaded = path_str in self.files if already_loaded: self.request_file_unload(name, path, request_lock=False) log.debug(f'Loading file: {path}') with self.__files_lock: self.files[path_str] = file = RuleFile(self, path) if not file.load(): # If the load has failed we remove it again. # Unloading is handled directly in the load function self.files.pop(path_str) log.warning(f'Failed to load {path_str}!') # signal that we have loaded the file but with a small delay asyncio.run_coroutine_threadsafe(set_load_failed(name), HABApp.core.const.loop) return None log.debug(f'File {path_str} successfully loaded!') # signal that we have loaded the file but with a small delay asyncio.run_coroutine_threadsafe(set_load_ok(name), HABApp.core.const.loop) # Do simple checks which prevent errors file.check_all_rules()
async def update_thing_config(self, path: Path, data=None): # we have to check the naming structure because we get file events for the whole folder _name = path.name.lower() if not _name.startswith('thing_') or not _name.endswith('.yml'): return None # only load if we don't supply the data if data is None: data = await async_get_things() # remove created items self.created_items.pop(path.name, None) created_items = self.created_items.setdefault(path.name, set()) # shedule cleanup self.do_cleanup.reset() # output file output_file = path.with_suffix('.items') if output_file.is_file(): output_file.unlink() # we also get events when the file gets deleted if not path.is_file(): log.debug(f'File {path} does not exist -> skipping Thing configuration!') return None log.debug(f'Loading {path}!') # load the config file yml = HABApp.parameters.parameter_files._yml_setup with path.open(mode='r', encoding='utf-8') as file: try: cfg = yml.load(file) except Exception as e: HABAppError(log).add_exception(e).dump() return None # validate configuration cfg = validate_cfg(cfg, path.name) if cfg is None: return None # if one entry has test set we show an overview of all the things if any(map(lambda x: x.test, cfg)): log_overview(data, THING_ALIAS, 'Thing overview') # process each thing part in the cfg for cfg_entry in cfg: test: bool = cfg_entry.test things = list(apply_filters(cfg_entry.filter, data, test)) # show a warning we found no Things if not things: log_warning(log, f'No things matched for {cfg_entry.filter}') continue # update thing configuration if cfg_entry.thing_config: await update_thing_cfg(cfg_entry.thing_config, things, test) try: # item creation for every thing create_items = {} shown_types = set() for thing in things: thing_context = {k: thing.get(alias, '') for k, alias in THING_ALIAS.items()} # create items without channel for item_cfg in cfg_entry.get_items(thing_context): name = item_cfg.name if name in create_items: raise ValueError(f'Duplicate item: {name}') create_items[name] = item_cfg # Channel overview, only if we have something configured if test and cfg_entry.channels: __thing_type = thing_context['thing_type'] if __thing_type not in shown_types: shown_types.add(__thing_type) log_overview(thing['channels'], CHANNEL_ALIAS, heading=f'Channels for {__thing_type}') # do channel things for channel_cfg in cfg_entry.channels: channels = apply_filters(channel_cfg.filter, thing['channels'], test) for channel in channels: channel_context = {k: channel.get(alias, '') for k, alias in CHANNEL_ALIAS.items()} channel_context.update(thing_context) for item_cfg in channel_cfg.get_items(channel_context): item_cfg.link = channel['uid'] name = item_cfg.name if name in create_items: raise ValueError(f'Duplicate item: {name}') create_items[name] = item_cfg # newline only if we create logs if test and (cfg_entry.create_items or cfg_entry.channels): log.info('') except InvalidItemNameError as e: HABAppError(log).add_exception(e).dump() continue # Create all items for item_cfg in create_items.values(): created = await create_item(item_cfg, test) if created: created_items.add(item_cfg.name) self.do_cleanup.reset() create_items_file(output_file, create_items)