diff --git a/.vscode/launch.json b/.vscode/launch.json index 3c69f503..7399e18e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,7 @@ "type": "python", "request": "launch", "program": "main.py", - "cwd": "${fileDirname}", + "cwd": "/workspaces/nspanel-lovelace-ui/nspanel-lovelace-ui/rootfs/usr/bin/mqtt-manager", "console": "integratedTerminal", "justMyCode": true } diff --git a/nspanel-lovelace-ui/config.yaml b/nspanel-lovelace-ui/config.yaml index 4dcd8eb0..5c367ce3 100644 --- a/nspanel-lovelace-ui/config.yaml +++ b/nspanel-lovelace-ui/config.yaml @@ -1,6 +1,6 @@ # https://developers.home-assistant.io/docs/add-ons/configuration#add-on-config name: NSPanel Lovelace UI Addon -version: "4.7.38" +version: "4.7.39" slug: nspanel-lovelace-ui description: NSPanel Lovelace UI Addon services: @@ -20,4 +20,4 @@ options: message: "Hello world..." schema: message: "str?" -image: "ghcr.io/jobr99/{arch}-nspanel-lovelace-ui" +#image: "ghcr.io/jobr99/{arch}-nspanel-lovelace-ui" diff --git a/nspanel-lovelace-ui/rootfs/usr/bin/mqtt-manager/ha_cards.py b/nspanel-lovelace-ui/rootfs/usr/bin/mqtt-manager/ha_cards.py index 27a9c3ed..dace7c9e 100644 --- a/nspanel-lovelace-ui/rootfs/usr/bin/mqtt-manager/ha_cards.py +++ b/nspanel-lovelace-ui/rootfs/usr/bin/mqtt-manager/ha_cards.py @@ -8,17 +8,24 @@ import dateutil.parser as dp import babel from libs.icon_mapping import get_icon_char from libs.helper import rgb_dec565, scale -import ha_template class HAEntity(panel_cards.Entity): def __init__(self, locale, config, panel): super().__init__(locale, config, panel) + def prerender(self): + # pre render templates + for p in ["color_overwrite", "icon_overwrite", "value_overwrite"]: + val = getattr(self, p) + if val and "ha:" in val: + print(f"yyyyyyyyyyy {val}") + libs.home_assistant.cache_template(val) + def render(self, cardType=""): if self.icon_overwrite and self.icon_overwrite.startswith("ha:"): - #icon_char = libs.home_assistant.render_template(self.icon_overwrite[3:]) - self.icon_overwrite = ha_template.render(self.icon_overwrite) + self.icon_overwrite = libs.home_assistant.get_template(self.icon_overwrite) + if self.etype in ["delete", "navigate", "iText"]: out = super().render() @@ -217,7 +224,8 @@ class HAEntity(panel_cards.Entity): name = "unsupported" if self.value_overwrite and self.value_overwrite.startswith("ha:"): - value = ha_template.render(self.value_overwrite[3:]) + value = libs.home_assistant.get_template(self.value_overwrite)[3:] + return f"~{entity_type_panel}~iid.{self.iid}~{icon_char}~{color}~{name}~{value}" @@ -273,6 +281,8 @@ class HACard(panel_cards.Card): class EntitiesCard(HACard): def __init__(self, locale, config, panel): super().__init__(locale, config, panel) + if len(self.entities) > 6 and self.type == "cardGrid": + self.type = "cardGrid2" def render(self): result = f"{self.title}~{self.gen_nav()}" @@ -287,6 +297,8 @@ class QRCard(HACard): def render(self): # TODO: Render QRCode as HomeAssistant Template #qrcode = apis.ha_api.render_template(qrcode) + if self.qrcode.startswith("ha:"): + self.qrcode = libs.home_assistant.get_template(self.qrcode)[3:] result = f"{self.title}~{self.gen_nav()}~{self.qrcode}" for e in self.entities: result += e.render() @@ -559,7 +571,6 @@ class Screensaver(HACard): libs.panel_cmd.statusUpdate(self.panel.sendTopic, f"{statusUpdateResult}~{icon1font}~{icon2font}") - def card_factory(locale, settings, panel): match settings["type"]: case 'cardEntities' | 'cardGrid': diff --git a/nspanel-lovelace-ui/rootfs/usr/bin/mqtt-manager/ha_template.py b/nspanel-lovelace-ui/rootfs/usr/bin/mqtt-manager/ha_template.py deleted file mode 100644 index 7e2219f4..00000000 --- a/nspanel-lovelace-ui/rootfs/usr/bin/mqtt-manager/ha_template.py +++ /dev/null @@ -1,30 +0,0 @@ -from jinja2 import Environment, BaseLoader -from typing import ( - Any -) -import libs.home_assistant - -def states(entity_id): - return libs.home_assistant.get_entity_data(entity_id).get("state") - -def state_attr(entity_id, attr): - return libs.home_assistant.get_entity_data(entity_id).get('attributes', []).get(attr) - -def iif(value: Any, if_true: Any = True, if_false: Any = False, if_none: Any = False) -> Any: - if value is None: - return if_none - if bool(value): - return if_true - return if_false - -func_dict = { - "states": states, - "state_attr": state_attr, - "iif": iif -} - -def render(template): - jinja_template = Environment(loader=BaseLoader()).from_string(template) - jinja_template.globals.update(func_dict) - template_string = jinja_template.render() - return template_string \ No newline at end of file diff --git a/nspanel-lovelace-ui/rootfs/usr/bin/mqtt-manager/libs/home_assistant.py b/nspanel-lovelace-ui/rootfs/usr/bin/mqtt-manager/libs/home_assistant.py index 29e3d117..d4d6263e 100644 --- a/nspanel-lovelace-ui/rootfs/usr/bin/mqtt-manager/libs/home_assistant.py +++ b/nspanel-lovelace-ui/rootfs/usr/bin/mqtt-manager/libs/home_assistant.py @@ -14,8 +14,10 @@ next_id = 0 request_all_states_id = 0 ws_connected = False home_assistant_entity_state_cache = {} +template_cache = {} response_buffer = {} + ON_CONNECT_HANDLER = None ON_DISCONNECT_HANDLER = None @@ -43,7 +45,7 @@ def register_on_disconnect_handler(handler): def on_message(ws, message): - global auth_ok, request_all_states_id, home_assistant_entity_state_cache, response_buffer + global auth_ok, request_all_states_id, home_assistant_entity_state_cache, response_buffer, template_cache json_msg = json.loads(message) if json_msg["type"] == "auth_required": authenticate_client() @@ -56,11 +58,19 @@ def on_message(ws, message): ON_CONNECT_HANDLER() # for templates elif json_msg["type"] == "event" and json_msg["id"] in response_buffer: - response_buffer[json_msg["id"]] = json_msg["event"] + template_cache[response_buffer[json_msg["id"]]] = { + "result": json_msg["event"]["result"], + "listener-entities": json_msg["event"]["listeners"]["entities"] + } elif json_msg["type"] == "event" and json_msg["event"]["event_type"] == "state_changed": entity_id = json_msg["event"]["data"]["entity_id"] home_assistant_entity_state_cache[entity_id] = json_msg["event"]["data"]["new_state"] send_entity_update(entity_id) + # rerender template + for template, template_cache_entry in template_cache.items(): + if entity_id in template_cache_entry.get("listener-entities", []): + cache_template(template) + elif json_msg["type"] == "result" and not json_msg["success"]: logging.error("Failed result: ") logging.error(json_msg) @@ -69,7 +79,7 @@ def on_message(ws, message): for entity in json_msg["result"]: home_assistant_entity_state_cache[entity["entity_id"]] = entity else: - if json_msg["id"] in response_buffer: + if json_msg["id"] in response_buffer and json_msg.get("result"): response_buffer[json_msg["id"]] = json_msg["result"] return None # Ignore success result messages else: @@ -205,32 +215,34 @@ def execute_script(entity_name: str, domain: str, service: str, service_data: di logging.exception("Failed to call Home Assisatant script.") return False -def render_template(template): +def cache_template(template): global next_id, response_buffer try: call_id = next_id - # request answer for this call - response_buffer[call_id] = True + response_buffer[call_id] = template msg = { - "id": next_id, + "id": call_id, "type": "render_template", - "template": 'template' + "template": template } - print(json.dumps(msg)) send_message(json.dumps(msg)) - # busy waiting for response with a timeout of 0.2 seconds - maybe there's a better way of doing this - mustend = time.time() + 0.2 - while time.time() < mustend: - if response_buffer[call_id] == True or response_buffer[call_id] is None: - #print(f'loooooooooop {time.time()}') - time.sleep(0.0001) - else: - return response_buffer[call_id]["result"] - raise TimeoutError("Did not recive respose in time to HA template render call") + return True except Exception as e: logging.exception("Failed to render template.") return False - return "" + +def get_template(template): + global template_cache + print(f"xxxxxxxxxxxx: {template_cache}") + if template in template_cache: + return template_cache[template].get("result") + else: + mustend = time.time() + 0.5 + while time.time() < mustend: + if template not in template_cache: + time.sleep(0.0001) + else: + return template_cache.get(template, []).get("result", "404") def get_entity_data(entity_id: str): if entity_id in home_assistant_entity_state_cache: diff --git a/nspanel-lovelace-ui/rootfs/usr/bin/mqtt-manager/panel.py b/nspanel-lovelace-ui/rootfs/usr/bin/mqtt-manager/panel.py index 85d1ee3d..881609a9 100644 --- a/nspanel-lovelace-ui/rootfs/usr/bin/mqtt-manager/panel.py +++ b/nspanel-lovelace-ui/rootfs/usr/bin/mqtt-manager/panel.py @@ -24,6 +24,7 @@ class LovelaceUIPanel: self.privious_cards = [] self.cards = {} self.hidden_cards = {} + self.screensaver = None self.navigate_keys = {} self.entity_iids = {} @@ -69,8 +70,29 @@ class LovelaceUIPanel: schedule_thread.daemon = True schedule_thread.start() + # check if ha state cache is already populated + ha_control.wait_for_ha_cache() + + #request templates on cards + for c in self.cards.values(): + if hasattr(c, "qrcode"): + if c.qrcode.startswith("ha:"): + libs.home_assistant.cache_template(c.qrcode) + for e in c.entities: + e.prerender() + for c in self.hidden_cards.values(): + if hasattr(c, "qrcode"): + if c.qrcode.startswith("ha:"): + libs.home_assistant.cache_template(c.qrcode) + for e in c.entities: + e.prerender() + self.screensaver = Screensaver(self.settings["locale"], self.settings["screensaver"], self) + for e in self.screensaver.entities: + e.prerender() + libs.panel_cmd.page_type(self.sendTopic, "pageStartup") + def schedule_thread_target(self): while True: self.schedule.exec_jobs() @@ -165,10 +187,7 @@ class LovelaceUIPanel: self.update_date() self.update_time() - # check if ha state cache is already populated - ha_control.wait_for_ha_cache() - - self.current_card = Screensaver(self.settings["locale"], self.settings["screensaver"], self) + self.current_card = self.screensaver self.render_current_page(switchPages=True) # send sleepTimeout @@ -176,11 +195,11 @@ class LovelaceUIPanel: if self.current_card.config.get("sleepTimeout"): sleepTimeout = self.current_card.config.get("sleepTimeout") libs.panel_cmd.timeout(self.sendTopic, sleepTimeout) - self.dimmode() + if msg[1] == "sleepReached": self.privious_cards.append(self.current_card) - self.current_card = Screensaver(self.settings["locale"], self.settings["screensaver"], self) + self.current_card = self.screensaver self.render_current_page(switchPages=True) if msg[1] == "renderCurrentPage": self.render_current_page(requested=True)