import secrets import string import apis def uuid(): alphabet = string.ascii_letters + string.digits return ''.join(secrets.choice(alphabet) for _ in range(10)) class Entity(object): def __init__(self, entity_input_config): self.uuid = f"uuid.{uuid()}" if type(entity_input_config) is not dict: self.entityId = "error" else: self.entityId = entity_input_config.get("entity", "unknown") self.nameOverride = entity_input_config.get("name") self.iconOverride = entity_input_config.get("icon") self.colorOverride = entity_input_config.get("color") self.status = entity_input_config.get("status") self.condState = entity_input_config.get("state") self.condStateNot = entity_input_config.get("state_not") self.condTemplate = entity_input_config.get("state_template") self.assumedState = entity_input_config.get("assumed_state", False) self.stype = entity_input_config.get("type") self.value = entity_input_config.get("value") self.font = entity_input_config.get("font") self.data = entity_input_config.get("data", {}) self.entity_input_config = entity_input_config class Card(object): def __init__(self, card_input_config, hidden=False): self.uuid = f"uuid.{uuid()}" self.uuid_prev = None self.uuid_next = None self.hidden = hidden self.raw_config = card_input_config self.cardType = card_input_config.get("type", "unknown") self.title = card_input_config.get("title", "unknown") self.key = card_input_config.get("key", "unknown") self.nav1Override = None if card_input_config.get("navItem1"): self.nav1Override = Entity(card_input_config.get("navItem1")) self.nav2Override = None if card_input_config.get("navItem2"): self.nav2Override = Entity(card_input_config.get("navItem2")) self.sleepTimeout = card_input_config.get("sleepTimeout") self.last_update = 0 self.cooldown = card_input_config.get("cooldown", 0) # for single entity card like climate or media self.entity = None if card_input_config.get("entity") is not None: self.entity = Entity(card_input_config) # for pages like grid or entities self.entities = [] for e in card_input_config.get("entities", []): self.entities.append(Entity(e)) self.id = f"{self.cardType}_{self.key}".replace(".","_").replace("~","_").replace(" ","_") def get_entity_names(self, uuid=False): entityIds = {} #apis.ha_api.log(f"test123 {self.entity.entityId}") if self.entity is not None: #entityIds[self.entity.uuid] = self.entity.entityId entityIds.setdefault(self.entity.uuid, []).append(self.entity.entityId) if self.entity.status is not None: #entityIds[self.entity.uuid] = self.entity.status entityIds.setdefault(self.entity.uuid, []).append(self.entity.status) for e in self.entities: #entityIds[e.uuid] = e.entityId entityIds.setdefault(e.uuid, []).append(e.entityId) if e.status is not None: #entityIds[e.uuid] = e.status entityIds.setdefault(e.uuid, []).append(e.status) # additional keys to check add_ent_keys = ['statusIcon1', 'statusIcon2', 'alarmControl'] for ent_key in add_ent_keys: val = self.raw_config.get(ent_key) if val is not None: #entityIds[f"{ent_key}."] = val.get("entity") entityIds.setdefault(self.entity.uuid, []).append(val.get("entity")) if uuid: return entityIds else: out = [] for l in entityIds.values(): out.extend(l) return out def get_entity_list(self): entitys = [] if self.entity is not None: entitys.append(self.entity) if self.entities: for e in self.entities: entitys.append(e) if self.nav1Override: entitys.append(self.nav1Override) if self.nav2Override: entitys.append(self.nav2Override) return entitys class LuiBackendConfig(object): def dict_recursive_update(self, source: dict, target: dict) -> dict: for sk, sv in source.items(): if sk in target and isinstance(target[sk], dict): target[sk] = self.dict_recursive_update(sv, target[sk]) else: target[sk] = sv return target def __init__(self, ha_api, config_in): apis.ha_api = ha_api self._config = {} self._config_cards = [] self._config_screensaver = None self._DEFAULT_CONFIG = { 'panelRecvTopic': "tele/tasmota_your_mqtt_topic/RESULT", 'panelSendTopic': "cmnd/tasmota_your_mqtt_topic/CustomSend", 'updateMode': "auto-notify", 'model': "eu", 'sleepTimeout': 20, 'sleepBrightness': 20, 'screenBrightness': 100, 'defaultBackgroundColor': "ha-dark", 'featureExperimentalSliders': False, 'sleepTracking': None, 'sleepTrackingZones': ["not_home", "off"], 'sleepOverride': None, 'locale': "en_US", 'quiet': True, 'timeFormat': "%H:%M", 'dateFormatBabel': "full", 'dateAdditionalTemplate': "", 'timeAdditionalTemplate': "", 'dateFormat': "%A, %d. %B %Y", 'cards': [{ 'type': 'cardEntities', 'entities': [{ 'entity': 'iText.', 'name': 'MQTT Config successful', 'icon': 'mdi:check', 'color:': [0, 255, 0], },{ 'entity': 'iText.', 'name': 'Continue adding', 'icon': 'mdi:arrow-right-bold', },{ 'entity': 'iText.', 'name': 'cards to your', 'icon': 'mdi:card', },{ 'entity': 'iText.', 'name': 'apps.yaml', 'icon': 'mdi:cog', }], 'title': 'Setup successful' }], 'screensaver': { 'type': 'screensaver', 'entity': 'weather.example', 'weatherUnit': 'celsius', 'forecastSkip': 0, 'weatherOverrideForecast1': None, 'weatherOverrideForecast2': None, 'weatherOverrideForecast3': None, 'weatherOverrideForecast4': None, 'doubleTapToUnlock': False, 'alternativeLayout': False, 'defaultCard': None, 'key': 'screensaver' }, 'hiddenCards': [] } self.load(config_in) def load(self, inconfig): apis.ha_api.log("Input config: %s", inconfig) self._config = self.dict_recursive_update(inconfig, self._DEFAULT_CONFIG) apis.ha_api.log("Loaded config: %s", self._config) # parse cards for card in self.get("cards"): self._config_cards.append(Card(card)) # setup prev and next uuids top_level_cards = list(filter(lambda card: not card.hidden, self._config_cards)) card_uuids = [card.uuid for card in top_level_cards] prev_uuids = card_uuids[-1:] + card_uuids[:-1] next_uuids = card_uuids[ 1:] + card_uuids[: 1] if len(card_uuids) > 1: for prev_uuids, card, next_uuids in zip(prev_uuids, top_level_cards, next_uuids): (card.uuid_prev, card.uuid_next) = (prev_uuids, next_uuids) # parse screensaver self._config_screensaver = Card(self.get("screensaver")) # parse hidden cards for card in self.get("hiddenCards"): self._config_cards.append(Card(card, hidden=True)) # all entites sorted by generated key, to be able to use short identifiers self._config_entites_table = {x.uuid: x for x in self.get_all_entitys()} self._config_card_table = {x.uuid: x for x in self._config_cards} def get(self, name): path = name.split(".") value = self._config for p in path: if value is not None: value = value.get(p, None) if value is not None: return value # try to get a value from default config value = self._DEFAULT_CONFIG for p in path: if value is not None: value = value.get(p, None) return value def get_all_entity_names(self): entities = [] for card in self._config_cards: entities.extend(card.get_entity_names()) entities.extend(self._config_screensaver.get_entity_names()) return entities def get_all_entitys(self): entities = [] for card in self._config_cards: entities.extend(card.get_entity_list()) return entities def search_card(self, id): id = id.replace("navigate.", "") if id.startswith("uuid"): return self.get_card_by_uuid(id) # legacy type_key for card in self._config_cards: if card.id == id: return card if self._config_screensaver.id == id: return self._config_screensaver # just search for key for card in self._config_cards: if card.key == id: return card if self._config_screensaver.key == id: return self._config_screensaver def get_default_card(self): defaultCard = self._config.get("screensaver.defaultCard") if defaultCard is not None: defaultCard = apis.ha_api.render_template(defaultCard) defaultCard = self.search_card(defaultCard) if defaultCard is not None: return defaultCard else: return self._config_cards[0] def get_card_by_uuid(self, uuid): return self._config_card_table.get(uuid)