From 200a0f5330296f128e67359f986feb834b729d36 Mon Sep 17 00:00:00 2001 From: Johannes Braun Date: Sun, 29 Jan 2023 19:43:31 +0100 Subject: [PATCH] implement #449 --- .../luibackend/controller.py | 12 ++++++++ apps/nspanel-lovelace-ui/luibackend/pages.py | 25 ++++++++++++++- docs/card-alarm.md | 2 +- docs/card-media.md | 2 +- docs/card-power.md | 2 +- docs/card-qr.md | 2 +- docs/card-unlock.md | 29 ++++++++++++++++++ docs/img/card-unlock.png | Bin 0 -> 15645 bytes mkdocs.yml | 1 + 9 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 docs/card-unlock.md create mode 100644 docs/img/card-unlock.png diff --git a/apps/nspanel-lovelace-ui/luibackend/controller.py b/apps/nspanel-lovelace-ui/luibackend/controller.py index a3822e9f..27d0d869 100644 --- a/apps/nspanel-lovelace-ui/luibackend/controller.py +++ b/apps/nspanel-lovelace-ui/luibackend/controller.py @@ -385,6 +385,18 @@ class LuiController(object): msg += f"- {apis.ha_api.get_entity(e).attributes.friendly_name}\r\n" self._pages_gen.send_message_page("opnSensorNotifyRes", "", msg, "", "") + # for cardUnlock + if button_type == "cardUnlock-unlock": + curCard = self._config.get_card_by_uuid(entity_id.replace('navigate.','')) + if curCard is not None: + if int(curCard.raw_config.get("pin")) == int(value): + dstCard = self._config.search_card(curCard.raw_config.get("destination")) + if dstCard is not None: + if dstCard.hidden: + self._previous_cards.append(self._current_card) + self._current_card = dstCard + self._pages_gen.render_card(self._current_card) + if button_type == "mode-preset_modes": entity = apis.ha_api.get_entity(entity_id) preset_mode = entity.attributes.preset_modes[int(value)] diff --git a/apps/nspanel-lovelace-ui/luibackend/pages.py b/apps/nspanel-lovelace-ui/luibackend/pages.py index 2e6c3190..3ad30936 100644 --- a/apps/nspanel-lovelace-ui/luibackend/pages.py +++ b/apps/nspanel-lovelace-ui/luibackend/pages.py @@ -121,6 +121,8 @@ class LuiPagesGen(object): self._send_mqtt_msg(f"date~{date}{addDateText}") def page_type(self, target_page): + if target_page == "cardUnlock": + target_page = "cardAlarm" self._send_mqtt_msg(f"pageType~{target_page}") def update_screensaver_weather(self, theme): @@ -368,7 +370,7 @@ class LuiPagesGen(object): ovalue = item.value if ovalue is not None: value = apis.ha_api.render_template(ovalue) - if locale == "he_IL" and any("\u0590" <= c <= "\u05EA" for c in name): + if self._locale == "he_IL" and any("\u0590" <= c <= "\u05EA" for c in name): name = name[::-1] return f"~{entityTypePanel}~{entityId}~{icon_id}~{color}~{name}~{value}" @@ -595,6 +597,20 @@ class LuiPagesGen(object): command = f"entityUpd~{title}~{navigation}~{item}{arm_buttons}~{icon}~{color}~{numpad}~{flashing}~{add_btn}" self._send_mqtt_msg(command) + def generate_unlock_page(self, navigation, item, title, destination, pin): + color = rgb_dec565([255,0,0]) + icon = get_icon_id("lock") + supported_modes = ["cardUnlock-unlock"] + + # add padding to arm buttons + arm_buttons = "" + for b in supported_modes: + arm_buttons += f'~{get_translation(self._locale, "frontend.ui.card.lock.unlock")}~{b}' + if len(supported_modes) < 4: + arm_buttons += "~"*((4-len(supported_modes))*2) + numpad = "enable" + command = f"entityUpd~{title}~{navigation}~{item}{arm_buttons}~{icon}~{color}~{numpad}~disable~" + self._send_mqtt_msg(command) def generate_qr_page(self, navigation, heading, items, cardType, qrcode): qrcode = apis.ha_api.render_template(qrcode) @@ -679,6 +695,12 @@ class LuiPagesGen(object): overwrite_supported_modes = card.raw_config.get("supportedModes") self.generate_alarm_page(navigation, card.title, card.entity, overwrite_supported_modes, alarmBtn) return + if card.cardType == "cardUnlock": + pin = card.raw_config.get("pin", 3830) + destination = card.raw_config.get("destination") + item = card.uuid + self.generate_unlock_page(navigation, item, card.title, destination, pin) + return if card.cardType == "screensaver": theme = card.raw_config.get("theme") self.update_screensaver_weather(theme) @@ -903,3 +925,4 @@ class LuiPagesGen(object): def send_message_page(self, ident, heading, msg, b1, b2): self._send_mqtt_msg(f"pageType~popupNotify") self._send_mqtt_msg(f"entityUpdateDetail~{ident}~{heading}~65535~{b1}~65535~{b2}~65535~{msg}~65535~0") + \ No newline at end of file diff --git a/docs/card-alarm.md b/docs/card-alarm.md index f18c1640..01af3b21 100644 --- a/docs/card-alarm.md +++ b/docs/card-alarm.md @@ -13,7 +13,7 @@ List of supported config keys of this card: key | optional | type | default | description -- | -- | -- | -- | -- -`type` | False | string | `None` | Used by navigate items +`type` | False | string | `None` | Type of the card `title` | True | string | `None` | Title of the Page `entity` | False | string | `None` | contains the entity of the current card `key` | True | string | `None` | Used by navigate items diff --git a/docs/card-media.md b/docs/card-media.md index d36b20d5..f85b444d 100644 --- a/docs/card-media.md +++ b/docs/card-media.md @@ -12,7 +12,7 @@ List of supported config keys of this card: key | optional | type | default | description -- | -- | -- | -- | -- -`type` | False | string | `None` | Used by navigate items +`type` | False | string | `None` | Type of the card `title` | True | string | `None` | Title of the Page `entity` | False | string | `None` | contains the entity of the current card `entities` | False | complex | `None` | contains a list of entities shown in the bottom row, supports all entities supported by cardGrid diff --git a/docs/card-power.md b/docs/card-power.md index 1a633717..932b986c 100644 --- a/docs/card-power.md +++ b/docs/card-power.md @@ -29,7 +29,7 @@ List of supported config keys of this card: key | optional | type | default | description -- | -- | -- | -- | -- -`type` | False | string | `None` | Used by navigate items +`type` | False | string | `None` | Type of the card `entities` | False | complex | `None` | contains entities of the card `title` | True | string | `None` | Title of the Page `key` | True | string | `None` | Used by navigate items diff --git a/docs/card-qr.md b/docs/card-qr.md index 881af98c..e2b53b31 100644 --- a/docs/card-qr.md +++ b/docs/card-qr.md @@ -20,7 +20,7 @@ List of supported config keys of this card: key | optional | type | default | description -- | -- | -- | -- | -- -`type` | False | string | `None` | Used by navigate items +`type` | False | string | `None` | Type of the card `entities` | False | complex | `None` | contains entities of the card, only valid on cardEntities and cardGrid and cardQR `title` | True | string | `None` | Title of the Page `key` | True | string | `None` | Used by navigate items diff --git a/docs/card-unlock.md b/docs/card-unlock.md new file mode 100644 index 00000000..6d82b9fb --- /dev/null +++ b/docs/card-unlock.md @@ -0,0 +1,29 @@ +# Unlock Card (v4.0 and higher) + +Can be used to navigate to create pin locked navigation to a hidden card. + +![card-unlock](img/card-unlock.png) + +```yaml + cards: + - type: cardUnlock + pin: 1234 + title: Admin Page + destination: navigate.adminpage + hiddenCards: + - type: cardGrid + title: Admin Page + key: adminpage + entities: + - entity: light.schreibtischlampe +``` + +List of supported config keys of this card: + +key | optional | type | default | description +-- | -- | -- | -- | -- +`type` | False | string | `None` | Type of the card +`title` | True | string | `None` | Title of the Page +`destination` | False | string | `None` | contains the navigation entity this card should navigate to on unlock +`pin` | False | string | 3830 | pin to unlock +`key` | True | string | `None` | Used by navigate items diff --git a/docs/img/card-unlock.png b/docs/img/card-unlock.png new file mode 100644 index 0000000000000000000000000000000000000000..6ff6fdd5b8c976816771e68eae31c89d20438db5 GIT binary patch literal 15645 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