Ejemplo n.º 1
0
    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()
Ejemplo n.º 2
0
    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()
Ejemplo n.º 3
0
    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()
Ejemplo n.º 4
0
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)
Ejemplo n.º 5
0
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
Ejemplo n.º 6
0
    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()
Ejemplo n.º 7
0
    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)