start reworking navigation paramters (#644)

* modify cardGrid/cardEntities nav

* rework backend nav for new style

* fix some bugs

* update other pages with new nav paramters

* readd readme part
This commit is contained in:
joBr99
2022-12-28 20:00:44 +01:00
committed by GitHub
parent ff792a0bb3
commit f0c5b5b429
16 changed files with 2567 additions and 278 deletions

View File

@@ -1 +1,2 @@
ha_api = None
ha_api = None
mqtt_api = None

View File

@@ -1,4 +1,6 @@
from itertools import pairwise
import uuid
import apis
class Entity(object):
@@ -20,8 +22,11 @@ class Entity(object):
self.entity_input_config = entity_input_config
class Card(object):
def __init__(self, card_input_config, pos=None):
self.pos = pos
def __init__(self, card_input_config, hidden=False):
self.uuid = f"uuid.{uuid.uuid4().hex}"
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")
@@ -35,7 +40,7 @@ class Card(object):
for e in card_input_config.get("entities", []):
self.entities.append(Entity(e))
self.id = f"{self.cardType}_{self.key}".replace(".","_").replace("~","_").replace(" ","_")
#self._ha_api.log(f"Created Card {self.cardType} with pos {pos} and id {self.id}")
#self._ha_api.log(f"Created Card {self.cardType} and id {self.id}")
def get_entity_names(self):
entityIds = []
@@ -81,7 +86,6 @@ class LuiBackendConfig(object):
self._config = {}
self._config_cards = []
self._config_screensaver = None
self._config_hidden_cards = []
self._DEFAULT_CONFIG = {
'panelRecvTopic': "tele/tasmota_your_mqtt_topic/RESULT",
@@ -148,20 +152,33 @@ class LuiBackendConfig(object):
self._config = self.dict_recursive_update(inconfig, self._DEFAULT_CONFIG)
apis.ha_api.log("Loaded config: %s", self._config)
# parse cards displayed on panel
pos = 0
# parse cards
for card in self.get("cards"):
self._config_cards.append(Card(card, pos))
pos = pos + 1
self._config_cards.append(Card(card))
# setup prev and next uuids
top_level_cards = filter(lambda card: not card.hidden, self._config_cards)
first_card = None
last_card = None
for cur, next in pairwise(top_level_cards):
if first_card is None:
first_card = cur
last_card = next
cur.uuid_next = next.uuid
next.uuid_prev = cur.uuid
first_card.uuid_prev = last_card.uuid
last_card.uuid_next = first_card.uuid
# parse screensaver
self._config_screensaver = Card(self.get("screensaver"))
# parse hidden pages that can be accessed through navigate
# parse hidden cards
for card in self.get("hiddenCards"):
self._config_hidden_cards.append(Card(card))
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(".")
@@ -182,8 +199,6 @@ class LuiBackendConfig(object):
entities = []
for card in self._config_cards:
entities.extend(card.get_entity_names())
for card in self._config_hidden_cards:
entities.extend(card.get_entity_names())
entities.extend(self._config_screensaver.get_entity_names())
return entities
@@ -191,21 +206,35 @@ class LuiBackendConfig(object):
entities = []
for card in self._config_cards:
entities.extend(card.get_entity_list())
for card in self._config_hidden_cards:
entities.extend(card.get_entity_list())
return entities
def getCard(self, pos):
card = self._config_cards[pos%len(self._config_cards)]
return card
def searchCard(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
for card in self._config_hidden_cards:
if card.id == id:
# 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")
defaultCard = apis.ha_api.render_template(defaultCard)
if defaultCard is not None:
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)

View File

@@ -1,9 +1,7 @@
import datetime
import apis
from helper import scale, pos_to_color, rgb_dec565
from pages import LuiPagesGen
class LuiController(object):
@@ -15,7 +13,7 @@ class LuiController(object):
self._current_card = self._config._config_screensaver
self._previous_cards = []
# first card (default, after startup)
self._previous_cards.append(self._config.getCard(0))
self._previous_cards.append(self._config.get_default_card())
self._pages_gen = LuiPagesGen(config, send_mqtt_msg)
@@ -207,9 +205,9 @@ class LuiController(object):
if dstCard is not None:
self._previous_cards = []
self._previous_cards.append(dstCard)
# set _previous_cards to first page in case it's empty
# set _previous_cards to default page in case it's empty
if len(self._previous_cards) == 0:
self._previous_cards.append(self._config.getCard(0))
self._previous_cards.append(self._config.get_default_card())
# check for double tap if configured and render current page
if self._config.get("screensaver.doubleTapToUnlock") and int(value) >= 2:
self._current_card = self._previous_cards.pop()
@@ -227,28 +225,14 @@ class LuiController(object):
if button_type == "bExit":
self._pages_gen.render_card(self._current_card)
if button_type == "bUp":
if self._previous_cards:
self._current_card = self._previous_cards.pop()
else:
self._current_card = self._config.getCard(0)
self._pages_gen.render_card(self._current_card)
if button_type == "bHome":
if self._previous_cards:
self._current_card = self._previous_cards[0]
self._previous_cards.clear()
else:
self._current_card = self._config.getCard(0)
self._pages_gen.render_card(self._current_card)
if button_type == "bNext":
card = self._config.getCard(self._current_card.pos+1)
self._current_card = card
self._pages_gen.render_card(card)
if button_type == "bPrev":
card = self._config.getCard(self._current_card.pos-1)
self._current_card = card
self._pages_gen.render_card(card)
#if button_type == "bHome":
# if self._previous_cards:
# self._current_card = self._previous_cards[0]
# self._previous_cards.clear()
# else:
# self._current_card = self._config.getCard(0)
# self._pages_gen.render_card(self._current_card)
elif entity_id == "updateDisplayNoYes" and value == "no":
self._pages_gen.render_card(self._current_card)
@@ -295,14 +279,25 @@ class LuiController(object):
entity_id = le.entityId
if entity_id.startswith('navigate'):
# internal navigation for next/prev
if entity_id.startswith('navigate.uuid'):
dstCard = self._config.get_card_by_uuid(entity_id.replace('navigate.',''))
# internal for navigation to nested pages
dstCard = self._config.searchCard(entity_id)
else:
dstCard = self._config.searchCard(entity_id)
if dstCard is not None:
self._previous_cards.append(self._current_card)
if dstCard.hidden:
self._previous_cards.append(self._current_card)
self._current_card = dstCard
self._pages_gen.render_card(self._current_card)
else:
apis.ha_api.log(f"No page with key {entity_id} found")
if entity_id.startswith('navUp'):
if self._previous_cards:
self._current_card = self._previous_cards.pop()
else:
self._current_card = self._config.get_default_card()
self._pages_gen.render_card(self._current_card)
elif entity_id.startswith('scene'):
apis.ha_api.get_entity(entity_id).call_service("turn_on")
elif entity_id.startswith('script'):

View File

@@ -1,18 +1,19 @@
import json
import apis
class LuiMqttListener(object):
def __init__(self, mqtt_api, topic, controller, updater):
def __init__(self, topic, controller, updater):
self._controller = controller
self._updater = updater
self._mqtt_api = mqtt_api
# Setup, mqtt subscription and callback
mqtt_api.mqtt_subscribe(topic=topic)
mqtt_api.listen_event(self.mqtt_event_callback, "MQTT_MESSAGE", topic=topic, namespace='mqtt')
apis.mqtt_api.mqtt_subscribe(topic=topic)
apis.mqtt_api.listen_event(self.mqtt_event_callback, "MQTT_MESSAGE", topic=topic, namespace='mqtt')
def mqtt_event_callback(self, event_name, data, kwargs):
self._mqtt_api.log(f'MQTT callback for: {data}')
apis.mqtt_api.log(f'MQTT callback for: {data}')
# Parse Json Message from Tasmota and strip out message from nextion display
data = json.loads(data["payload"])
if("nlui_driver_version" in data):
@@ -22,7 +23,7 @@ class LuiMqttListener(object):
if("CustomRecv" not in data):
return
msg = data["CustomRecv"]
self._mqtt_api.log(f"Received Message from Screen: {msg}")
apis.mqtt_api.log(f"Received Message from Screen: {msg}")
# Split message into parts seperated by ","
msg = msg.split(",")
# run action based on received command
@@ -30,9 +31,7 @@ class LuiMqttListener(object):
if msg[1] == "startup":
self._updater.request_berry_driver_version()
display_firmware_version = int(msg[2])
model = None
if display_firmware_version >= 23:
model = msg[3]
model = msg[3]
self._updater.set_current_display_firmware_version(display_firmware_version, model)
# check for updates
msg_send = self._updater.check_updates()
@@ -60,9 +59,8 @@ class LuiMqttListener(object):
self._controller.detail_open(msg[2], msg[3])
class LuiMqttSender(object):
def __init__(self, api, mqttapi, topic_send):
def __init__(self, api, topic_send):
self._ha_api = api
self._mqtt_api = mqttapi
self._topic_send = topic_send
self._prev_msg = ""
@@ -73,4 +71,4 @@ class LuiMqttSender(object):
if topic is None:
topic = self._topic_send
self._ha_api.log(f"Sending MQTT Message: {msg}")
self._mqtt_api.mqtt_publish(topic, msg)
apis.mqtt_api.mqtt_publish(topic, msg)

View File

@@ -9,6 +9,7 @@ from icons import get_icon, get_icon_ha
from icons import get_action_icon
from helper import scale, rgb_dec565, rgb_brightness, get_attr_safe, convert_temperature
from localization import get_translation
from config import Entity
# check Babel
import importlib
@@ -32,43 +33,46 @@ class LuiPagesGen(object):
for overwrite_state, overwrite_val in overwrite.items():
if overwrite_state == state:
return rgb_dec565(overwrite_val)
if isinstance(entity, str):
default_color = rgb_dec565([68, 115, 158])
return default_color
else:
attr = entity.attributes
default_color_on = rgb_dec565([253, 216, 53])
default_color_off = rgb_dec565([68, 115, 158])
icon_color = default_color_on if entity.state in ["on", "unlocked", "above_horizon", "home", "active"] else default_color_off
attr = entity.attributes
default_color_on = rgb_dec565([253, 216, 53])
default_color_off = rgb_dec565([68, 115, 158])
icon_color = default_color_on if entity.state in ["on", "unlocked", "above_horizon", "home", "active"] else default_color_off
if ha_type == "alarm_control_panel":
if entity.state == "disarmed":
icon_color = rgb_dec565([13,160,53])
if entity.state == "arming":
icon_color = rgb_dec565([244,180,0])
if entity.state in ["armed_home", "armed_away", "armed_night", "armed_vacation", "pending", "triggered"]:
icon_color = rgb_dec565([223,76,30])
if ha_type == "alarm_control_panel":
if entity.state == "disarmed":
icon_color = rgb_dec565([13,160,53])
if entity.state == "arming":
icon_color = rgb_dec565([244,180,0])
if entity.state in ["armed_home", "armed_away", "armed_night", "armed_vacation", "pending", "triggered"]:
icon_color = rgb_dec565([223,76,30])
if ha_type == "climate":
if entity.state in ["auto", "heat_cool"]:
icon_color = 1024
if entity.state == "heat":
icon_color = 64512
if entity.state == "off":
icon_color = 35921
if entity.state == "cool":
icon_color = 11487
if entity.state == "dry":
icon_color = 60897
if entity.state == "fan_only":
icon_color = 35921
if ha_type == "climate":
if entity.state in ["auto", "heat_cool"]:
icon_color = 1024
if entity.state == "heat":
icon_color = 64512
if entity.state == "off":
icon_color = 35921
if entity.state == "cool":
icon_color = 11487
if entity.state == "dry":
icon_color = 60897
if entity.state == "fan_only":
icon_color = 35921
if "rgb_color" in attr:
color = attr.rgb_color
if "brightness" in attr:
color = rgb_brightness(color, attr.brightness)
icon_color = rgb_dec565(color)
elif "brightness" in attr:
color = rgb_brightness([253, 216, 53], attr.brightness)
icon_color = rgb_dec565(color)
return icon_color
if "rgb_color" in attr:
color = attr.rgb_color
if "brightness" in attr:
color = rgb_brightness(color, attr.brightness)
icon_color = rgb_dec565(color)
elif "brightness" in attr:
color = rgb_brightness([253, 216, 53], attr.brightness)
icon_color = rgb_dec565(color)
return icon_color
def update_time(self, kwargs):
time = datetime.datetime.now().strftime(self._config.get("timeFormat"))
@@ -201,7 +205,7 @@ class LuiPagesGen(object):
state = None
self._send_mqtt_msg(get_screensaver_color_output(theme=theme, state=state))
def generate_entities_item(self, item, cardType, temp_unit=""):
def generate_entities_item(self, item, cardType="cardGrid", temp_unit=""):
entityId = item.entityId
icon = item.iconOverride
colorOverride = item.colorOverride
@@ -230,7 +234,8 @@ class LuiPagesGen(object):
if icon_res[-1] == ".":
icon_res = icon_res[:-1]
else:
icon_color = rgb_dec565(colorOverride) if colorOverride is not None and type(colorOverride) is list else 17299
#icon_color = rgb_dec565(colorOverride) if colorOverride is not None and type(colorOverride) is list else 17299
icon_color = self.get_entity_color(entityId, overwrite=colorOverride)
return f"~button~{entityId}~{icon_res}~{icon_color}~{name}~{text}"
else:
return f"~text~{entityId}~{get_icon_id('alert-circle-outline')}~17299~page not found~"
@@ -631,20 +636,34 @@ class LuiPagesGen(object):
command += f"~{icon_color}~{icon}~{speed}~{entity.state}"
self._send_mqtt_msg(command)
def render_card(self, card, send_page_type=True):
apis.ha_api.log(f"Started rendering of page {card.pos} with type {card.cardType}")
l = 1
r = 1
def render_card(self, card, send_page_type=True):
l = self.generate_entities_item(Entity(
{
'entity': f'navigate.{card.uuid_prev}',
'icon': 'mdi:arrow-left-bold',
'color': [255, 255, 255],
}
))[1:]
r = self.generate_entities_item(Entity(
{
'entity': f'navigate.{card.uuid_next}',
'icon': 'mdi:arrow-right-bold',
'color': [255, 255, 255],
}
))[1:]
if len(self._config._config_cards) == 1:
l = 0
r = 0
if card.pos is None:
l = 2
r = 0
if self._config.get("homeButton"):
r = 2
navigation = f"{l}|{r}"
l = "delete~~~~~"
r = "delete~~~~~"
if card.hidden:
l = f"x~navUp~{get_icon_id('mdi:arrow-up-bold')}~65535~~"
r = "delete~~~~~"
# r = 0
# if self._config.get("homeButton"):
# r = 2
navigation = f"{l}~{r}"
# Switch to page
if send_page_type:

View File

@@ -5,43 +5,44 @@ from luibackend.controller import LuiController
from luibackend.mqtt import LuiMqttListener, LuiMqttSender
from luibackend.updater import Updater
import apis
class NsPanelLovelaceUIManager(hass.Hass):
def initialize(self):
self.log('Starting')
mqtt_api = self._mqtt_api = self.get_plugin_api("MQTT")
apis.ha_api = self
apis.mqtt_api = self.get_plugin_api("MQTT")
cfg = self._cfg = LuiBackendConfig(self, self.args["config"])
topic_send = cfg.get("panelSendTopic")
mqttsend = LuiMqttSender(self, mqtt_api, topic_send)
topic_recv = cfg.get("panelRecvTopic")
mqttsend = LuiMqttSender(self, topic_send)
# Request Tasmota Driver Version
mqtt_api.mqtt_publish(topic_send.replace("CustomSend", "GetDriverVersion"), "x")
apis.mqtt_api.mqtt_publish(topic_send.replace("CustomSend", "GetDriverVersion"), "x")
controller = LuiController(cfg, mqttsend.send_mqtt_msg)
desired_tasmota_driver_version = 8
desired_display_firmware_version = 46
version = "v3.7.0"
model = cfg.get("model")
if model == "us-l":
# us landscape version
desired_display_firmware_url = f"http://nspanel.pky.eu/lovelace-ui/github/nspanel-us-l-{version}.tft"
elif model == "us-p":
# us portrait version
desired_display_firmware_url = f"http://nspanel.pky.eu/lovelace-ui/github/nspanel-us-p-{version}.tft"
else:
# eu version
desired_display_firmware_url = f"http://nspanel.pky.eu/lovelace-ui/github/nspanel-{version}.tft"
desired_tasmota_driver_version = 8
match model:
case "us-l":
desired_display_firmware_url = f"http://nspanel.pky.eu/lovelace-ui/github/nspanel-us-l-{version}.tft"
case "us-p":
desired_display_firmware_url = f"http://nspanel.pky.eu/lovelace-ui/github/nspanel-us-p-{version}.tft"
case _:
desired_display_firmware_url = f"http://nspanel.pky.eu/lovelace-ui/github/nspanel-{version}.tft"
desired_tasmota_driver_url = "https://raw.githubusercontent.com/joBr99/nspanel-lovelace-ui/main/tasmota/autoexec.be"
mode = cfg.get("updateMode")
topic_send = cfg.get("panelSendTopic")
updater = Updater(self.log, mqttsend.send_mqtt_msg, topic_send, mode, desired_display_firmware_version, model, desired_display_firmware_url, desired_tasmota_driver_version, desired_tasmota_driver_url)
topic_recv = cfg.get("panelRecvTopic")
LuiMqttListener(mqtt_api, topic_recv, controller, updater)
LuiMqttListener(topic_recv, controller, updater)
self.log('Started')