mirror of
https://github.com/joBr99/nspanel-lovelace-ui.git
synced 2025-12-21 06:54:24 +01:00
add queue for outgoing messages
This commit is contained in:
@@ -297,7 +297,7 @@ class EntitiesCard(HACard):
|
||||
result = f"{self.title}~{self.gen_nav()}"
|
||||
for e in self.entities:
|
||||
result += e.render(cardType=self.type)
|
||||
libs.panel_cmd.entityUpd(self.panel.sendTopic, result)
|
||||
libs.panel_cmd.entityUpd(self.panel.msg_out_queue, self.panel.sendTopic, result)
|
||||
|
||||
class QRCard(HACard):
|
||||
def __init__(self, locale, config, panel):
|
||||
@@ -311,7 +311,7 @@ class QRCard(HACard):
|
||||
result = f"{self.title}~{self.gen_nav()}~{self.qrcode}"
|
||||
for e in self.entities:
|
||||
result += e.render()
|
||||
libs.panel_cmd.entityUpd(self.panel.sendTopic, result)
|
||||
libs.panel_cmd.entityUpd(self.panel.msg_out_queue, self.panel.sendTopic, result)
|
||||
|
||||
class PowerCard(HACard):
|
||||
def __init__(self, locale, config, panel):
|
||||
@@ -329,7 +329,7 @@ class PowerCard(HACard):
|
||||
# if isinstance(speed, str):
|
||||
# speed = apis.ha_api.render_template(speed)
|
||||
result += f"~{speed}"
|
||||
libs.panel_cmd.entityUpd(self.panel.sendTopic, result)
|
||||
libs.panel_cmd.entityUpd(self.panel.msg_out_queue, self.panel.sendTopic, result)
|
||||
|
||||
class MediaCard(HACard):
|
||||
def __init__(self, locale, config, panel):
|
||||
@@ -366,7 +366,7 @@ class MediaCard(HACard):
|
||||
for e in self.entities[1:]:
|
||||
button_str += e.render()
|
||||
result = f"{self.title}~{self.gen_nav()}~{main_entity.entity_id}~{title}~~{author}~~{volume}~{iconplaypause}~{onoffbutton}~{shuffleBtn}{media_icon}{button_str}"
|
||||
libs.panel_cmd.entityUpd(self.panel.sendTopic, result)
|
||||
libs.panel_cmd.entityUpd(self.panel.msg_out_queue, self.panel.sendTopic, result)
|
||||
|
||||
class ClimateCard(HACard):
|
||||
def __init__(self, locale, config, panel):
|
||||
@@ -461,7 +461,7 @@ class ClimateCard(HACard):
|
||||
detailPage = "0"
|
||||
|
||||
result = f"{self.title}~{self.gen_nav()}~{main_entity.entity_id}~{current_temp} {temperature_unit}~{dest_temp}~{state_value}~{min_temp}~{max_temp}~{step_temp}{icon_res}~{currently_translation}~{state_translation}~{action_translation}~{temperature_unit_icon}~{dest_temp2}~{detailPage}"
|
||||
libs.panel_cmd.entityUpd(self.panel.sendTopic, result)
|
||||
libs.panel_cmd.entityUpd(self.panel.msg_out_queue, self.panel.sendTopic, result)
|
||||
|
||||
class AlarmCard(HACard):
|
||||
def __init__(self, locale, config, panel):
|
||||
@@ -531,7 +531,7 @@ class AlarmCard(HACard):
|
||||
if len(supported_modes) < 4:
|
||||
arm_buttons += "~"*((4-len(supported_modes))*2)
|
||||
result = f"{self.title}~{self.gen_nav()}~{main_entity.entity_id}{arm_buttons}~{icon}~{color}~{numpad}~{flashing}~{add_btn}"
|
||||
libs.panel_cmd.entityUpd(self.panel.sendTopic, result)
|
||||
libs.panel_cmd.entityUpd(self.panel.msg_out_queue, self.panel.sendTopic, result)
|
||||
|
||||
|
||||
class UnlockCard(HACard):
|
||||
@@ -555,7 +555,7 @@ class UnlockCard(HACard):
|
||||
numpad = "enable"
|
||||
|
||||
result = f"{self.title}~{self.gen_nav()}~{entity_id}{arm_buttons}~{icon}~{color}~{numpad}~disable~"
|
||||
libs.panel_cmd.entityUpd(self.panel.sendTopic, result)
|
||||
libs.panel_cmd.entityUpd(self.panel.msg_out_queue, self.panel.sendTopic, result)
|
||||
|
||||
class Screensaver(HACard):
|
||||
def __init__(self, locale, config, panel):
|
||||
@@ -583,7 +583,7 @@ class Screensaver(HACard):
|
||||
result = ""
|
||||
for e in self.entities:
|
||||
result += e.render(cardType=self.type)
|
||||
libs.panel_cmd.weatherUpdate(self.panel.sendTopic, result[1:])
|
||||
libs.panel_cmd.weatherUpdate(self.panel.msg_out_queue, self.panel.sendTopic, result[1:])
|
||||
|
||||
statusUpdateResult = ""
|
||||
icon1font = ""
|
||||
@@ -601,7 +601,7 @@ class Screensaver(HACard):
|
||||
else:
|
||||
statusUpdateResult += "~~"
|
||||
|
||||
libs.panel_cmd.statusUpdate(self.panel.sendTopic, f"{statusUpdateResult}~{icon1font}~{icon2font}")
|
||||
libs.panel_cmd.statusUpdate(self.panel.msg_out_queue, self.panel.sendTopic, f"{statusUpdateResult}~{icon1font}~{icon2font}")
|
||||
|
||||
|
||||
def card_factory(locale, settings, panel):
|
||||
@@ -625,7 +625,7 @@ def card_factory(locale, settings, panel):
|
||||
return "NotImplemented", None
|
||||
return card.iid, card
|
||||
|
||||
def detail_open(locale, detail_type, ha_entity_id, entity_id, sendTopic=None, options_list=None):
|
||||
def detail_open(locale, detail_type, ha_entity_id, entity_id, msg_out_queue, sendTopic=None, options_list=None):
|
||||
data = libs.home_assistant.get_entity_data(ha_entity_id)
|
||||
if data:
|
||||
state = data.get("state")
|
||||
@@ -804,8 +804,8 @@ def detail_open(locale, detail_type, ha_entity_id, entity_id, sendTopic=None, op
|
||||
#update timer in a second
|
||||
def update_time():
|
||||
time.sleep(1)
|
||||
out = detail_open(locale, detail_type, ha_entity_id, entity_id, sendTopic=sendTopic)
|
||||
libs.panel_cmd.entityUpdateDetail(sendTopic, out)
|
||||
out = detail_open(locale, detail_type, ha_entity_id, entity_id, msg_out_queue, sendTopic=sendTopic)
|
||||
libs.panel_cmd.entityUpdateDetail(msg_out_queue, sendTopic, out)
|
||||
tt = threading.Thread(target=update_time, args=())
|
||||
tt.daemon = True
|
||||
tt.start()
|
||||
|
||||
@@ -5,45 +5,44 @@ def init(mqtt_client_from_manager):
|
||||
mqtt_client = mqtt_client_from_manager
|
||||
|
||||
|
||||
def custom_send(topic, msg):
|
||||
global mqtt_client
|
||||
mqtt_client.publish(topic, msg)
|
||||
def custom_send(msg_out_queue, topic, msg):
|
||||
msg_out_queue.put((topic, msg))
|
||||
logging.debug("Sent Message to NsPanel (%s): %s", topic, msg)
|
||||
|
||||
|
||||
def page_type(topic, target_page):
|
||||
def page_type(msg_out_queue, topic, target_page):
|
||||
if target_page == "cardUnlock":
|
||||
target_page = "cardAlarm"
|
||||
custom_send(topic, f"pageType~{target_page}")
|
||||
custom_send(msg_out_queue, topic, f"pageType~{target_page}")
|
||||
|
||||
|
||||
def send_time(topic, time, addTimeText=""):
|
||||
custom_send(topic, f"time~{time}~{addTimeText}")
|
||||
def send_time(msg_out_queue, topic, time, addTimeText=""):
|
||||
custom_send(msg_out_queue, topic, f"time~{time}~{addTimeText}")
|
||||
|
||||
|
||||
def send_date(topic, date):
|
||||
custom_send(topic, f"date~{date}")
|
||||
def send_date(msg_out_queue, topic, date):
|
||||
custom_send(msg_out_queue, topic, f"date~{date}")
|
||||
|
||||
|
||||
def entityUpd(topic, data):
|
||||
custom_send(topic, f"entityUpd~{data}")
|
||||
def entityUpd(msg_out_queue, topic, data):
|
||||
custom_send(msg_out_queue, topic, f"entityUpd~{data}")
|
||||
|
||||
def weatherUpdate(topic, data):
|
||||
custom_send(topic, f"weatherUpdate~{data}")
|
||||
def weatherUpdate(msg_out_queue, topic, data):
|
||||
custom_send(msg_out_queue, topic, f"weatherUpdate~{data}")
|
||||
|
||||
def timeout(topic, timeout):
|
||||
custom_send(topic, f"timeout~{timeout}")
|
||||
def timeout(msg_out_queue, topic, timeout):
|
||||
custom_send(msg_out_queue, topic, f"timeout~{timeout}")
|
||||
|
||||
def dimmode(topic, dimValue, dimValueNormal, backgroundColor, fontColor, featExperimentalSliders):
|
||||
def dimmode(msg_out_queue, topic, dimValue, dimValueNormal, backgroundColor, fontColor, featExperimentalSliders):
|
||||
if dimValue==dimValueNormal:
|
||||
dimValue=dimValue-1
|
||||
custom_send(topic, f"dimmode~{dimValue}~{dimValueNormal}~{backgroundColor}~{fontColor}~{featExperimentalSliders}")
|
||||
custom_send(msg_out_queue, topic, f"dimmode~{dimValue}~{dimValueNormal}~{backgroundColor}~{fontColor}~{featExperimentalSliders}")
|
||||
|
||||
def entityUpdateDetail(topic, data):
|
||||
custom_send(topic, f"entityUpdateDetail~{data}")
|
||||
def entityUpdateDetail(msg_out_queue, topic, data):
|
||||
custom_send(msg_out_queue, topic, f"entityUpdateDetail~{data}")
|
||||
|
||||
def entityUpdateDetail2(topic, data):
|
||||
custom_send(topic, f"entityUpdateDetail2~{data}")
|
||||
def entityUpdateDetail2(msg_out_queue, topic, data):
|
||||
custom_send(msg_out_queue, topic, f"entityUpdateDetail2~{data}")
|
||||
|
||||
def statusUpdate(topic, data):
|
||||
custom_send(topic, f"statusUpdate~{data}")
|
||||
def statusUpdate(msg_out_queue, topic, data):
|
||||
custom_send(msg_out_queue, topic, f"statusUpdate~{data}")
|
||||
@@ -1,224 +1,161 @@
|
||||
#!/usr/bin/env python
|
||||
import logging
|
||||
import paho.mqtt.client as mqtt
|
||||
import time
|
||||
import json
|
||||
import subprocess
|
||||
import libs.home_assistant
|
||||
import libs.panel_cmd
|
||||
import yaml
|
||||
from uuid import getnode as get_mac
|
||||
from panel import LovelaceUIPanel
|
||||
import os
|
||||
import threading
|
||||
from watchdog.events import FileSystemEventHandler
|
||||
from watchdog.observers import Observer
|
||||
import signal
|
||||
import sys
|
||||
from queue import Queue
|
||||
|
||||
logging.getLogger("watchdog").propagate = False
|
||||
|
||||
settings = {}
|
||||
panels = {}
|
||||
panel_queues = {}
|
||||
last_settings_file_mtime = 0
|
||||
mqtt_connect_time = 0
|
||||
has_sent_reload_command = False
|
||||
mqtt_client_name = "NSPanelLovelaceManager_" + str(get_mac())
|
||||
client = mqtt.Client(mqtt_client_name)
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
def on_connect(client, userdata, flags, rc):
|
||||
global settings
|
||||
logging.info("Connected to MQTT Server")
|
||||
# subscribe to panelRecvTopic of each panel
|
||||
for settings_panel in settings["nspanels"].values():
|
||||
client.subscribe(settings_panel["panelRecvTopic"])
|
||||
|
||||
def on_ha_update(entity_id):
|
||||
global panel_queues
|
||||
for queue in panel_queues.values():
|
||||
queue.put(("HA:", entity_id))
|
||||
|
||||
def on_message(client, userdata, msg):
|
||||
global panel_queues
|
||||
try:
|
||||
if msg.payload.decode() == "":
|
||||
return
|
||||
parts = msg.topic.split('/')
|
||||
if msg.topic in panel_queues.keys():
|
||||
data = json.loads(msg.payload.decode('utf-8'))
|
||||
if "CustomRecv" in data:
|
||||
queue = panel_queues[msg.topic]
|
||||
queue.put(("MQTT:", data["CustomRecv"]))
|
||||
else:
|
||||
logging.debug("Received unhandled message on topic: %s", msg.topic)
|
||||
|
||||
except Exception: # pylint: disable=broad-exception-caught
|
||||
logging.exception("Something went wrong during processing of message:")
|
||||
try:
|
||||
logging.error(msg.payload.decode('utf-8'))
|
||||
except: # pylint: disable=bare-except
|
||||
logging.error(
|
||||
"Something went wrong when processing the exception message, couldn't decode payload to utf-8.")
|
||||
|
||||
def get_config_file():
|
||||
CONFIG_FILE = os.getenv('CONFIG_FILE')
|
||||
if not CONFIG_FILE:
|
||||
CONFIG_FILE = './panels.yaml'
|
||||
return CONFIG_FILE
|
||||
|
||||
def get_config(file):
|
||||
global settings
|
||||
|
||||
try:
|
||||
with open(file, 'r', encoding="utf8") as file:
|
||||
settings = yaml.safe_load(file)
|
||||
except yaml.YAMLError as exc:
|
||||
print ("Error while parsing YAML file:")
|
||||
if hasattr(exc, 'problem_mark'):
|
||||
if exc.context != None:
|
||||
print (' parser says\n' + str(exc.problem_mark) + '\n ' +
|
||||
str(exc.problem) + ' ' + str(exc.context) +
|
||||
'\nPlease correct data and retry.')
|
||||
else:
|
||||
print (' parser says\n' + str(exc.problem_mark) + '\n ' +
|
||||
str(exc.problem) + '\nPlease correct data and retry.')
|
||||
else:
|
||||
print ("Something went wrong while parsing yaml file")
|
||||
return False
|
||||
|
||||
if not settings.get("mqtt_username"):
|
||||
settings["mqtt_username"] = os.getenv('MQTT_USER')
|
||||
if not settings.get("mqtt_password"):
|
||||
settings["mqtt_password"] = os.getenv('MQTT_PASS')
|
||||
if not settings.get("mqtt_port"):
|
||||
settings["mqtt_port"] = os.getenv('MQTT_PORT')
|
||||
if not settings.get("mqtt_server"):
|
||||
settings["mqtt_server"] = os.getenv('MQTT_SERVER')
|
||||
|
||||
|
||||
settings["is_addon"] = False
|
||||
|
||||
if not settings.get("home_assistant_token"):
|
||||
st = os.getenv('SUPERVISOR_TOKEN')
|
||||
if st and "home_assistant_token" not in settings and "home_assistant_address" not in settings:
|
||||
settings["home_assistant_token"] = st
|
||||
settings["home_assistant_address"] = "http://supervisor"
|
||||
settings["is_addon"] = True
|
||||
return True
|
||||
|
||||
def connect():
|
||||
global settings, home_assistant, client
|
||||
client.on_connect = on_connect
|
||||
client.on_message = on_message
|
||||
client.username_pw_set(
|
||||
settings["mqtt_username"], settings["mqtt_password"])
|
||||
# Wait for connection
|
||||
connection_return_code = 0
|
||||
mqtt_server = settings["mqtt_server"]
|
||||
mqtt_port = int(settings["mqtt_port"])
|
||||
logging.info("Connecting to %s:%i as %s",
|
||||
mqtt_server, mqtt_port, mqtt_client_name)
|
||||
while True:
|
||||
try:
|
||||
client.connect(mqtt_server, mqtt_port, 5)
|
||||
break # Connection call did not raise exception, connection is sucessfull
|
||||
except: # pylint: disable=bare-except
|
||||
logging.exception(
|
||||
"Failed to connect to MQTT %s:%i. Will try again in 10 seconds. Code: %s", mqtt_server, mqtt_port, connection_return_code)
|
||||
time.sleep(10.)
|
||||
|
||||
# MQTT Connected, start APIs if configured
|
||||
if settings["home_assistant_address"] != "" and settings["home_assistant_token"] != "":
|
||||
libs.home_assistant.init(settings, on_ha_update)
|
||||
libs.home_assistant.connect()
|
||||
else:
|
||||
logging.info("Home Assistant values not configured, will not connect.")
|
||||
|
||||
libs.panel_cmd.init(client)
|
||||
|
||||
setup_panels()
|
||||
|
||||
def loop():
|
||||
global client
|
||||
# Loop MQTT
|
||||
client.loop_forever()
|
||||
|
||||
def setup_panels():
|
||||
global settings, panel_queues
|
||||
# Create NsPanel object
|
||||
for name, settings_panel in settings["nspanels"].items():
|
||||
if "timeZone" not in settings_panel:
|
||||
settings_panel["timeZone"] = settings.get("timeZone", "Europe/Berlin")
|
||||
if "locale" not in settings_panel:
|
||||
settings_panel["timezone"] = settings.get("locale", "en_US")
|
||||
if "hiddenCards" not in settings_panel:
|
||||
settings_panel["hiddenCards"] = settings.get("hiddenCards", [])
|
||||
|
||||
#panels[name] = LovelaceUIPanel(name, settings_panel)
|
||||
|
||||
mqtt_queue = Queue(maxsize=20)
|
||||
panel_queues[settings_panel["panelRecvTopic"]] = mqtt_queue
|
||||
panel_thread = threading.Thread(target=panel_thread_target, args=(mqtt_queue, name, settings_panel))
|
||||
panel_thread.daemon = True
|
||||
|
||||
panel_thread.start()
|
||||
|
||||
def panel_thread_target(queue, name, settings_panel):
|
||||
panel = LovelaceUIPanel(name, settings_panel)
|
||||
while True:
|
||||
msg = queue.get()
|
||||
#print(msg)
|
||||
if msg[0] == "MQTT:":
|
||||
panel.customrecv_event_callback(msg[1])
|
||||
elif msg[0] == "HA:":
|
||||
panel.ha_event_callback(msg[1])
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def config_watch():
|
||||
class ConfigChangeEventHandler(FileSystemEventHandler):
|
||||
def __init__(self, base_paths):
|
||||
self.base_paths = base_paths
|
||||
|
||||
def dispatch(self, event):
|
||||
for base_path in self.base_paths:
|
||||
if event.src_path.endswith(base_path):
|
||||
super(ConfigChangeEventHandler, self).dispatch(event)
|
||||
return
|
||||
|
||||
def on_modified(self, event):
|
||||
logging.info('Modification detected. Reloading panels.')
|
||||
pid = os.getpid()
|
||||
os.kill(pid, signal.SIGTERM)
|
||||
|
||||
logging.info('Watching for changes in config file')
|
||||
project_files = []
|
||||
project_files.append(get_config_file())
|
||||
handler = ConfigChangeEventHandler(project_files)
|
||||
observer = Observer()
|
||||
observer.schedule(handler, path=os.path.dirname(get_config_file()), recursive=True)
|
||||
observer.start()
|
||||
while True:
|
||||
time.sleep(1)
|
||||
|
||||
def signal_handler(signum, frame):
|
||||
logging.info(f"Received signal {signum}. Initiating restart...")
|
||||
python = sys.executable
|
||||
os.execl(python, python, *sys.argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
signal.signal(signal.SIGTERM, signal_handler)
|
||||
threading.Thread(target=config_watch).start()
|
||||
if (get_config(get_config_file())):
|
||||
connect()
|
||||
loop()
|
||||
else:
|
||||
while True:
|
||||
#!/usr/bin/env python
|
||||
import logging
|
||||
import time
|
||||
import subprocess
|
||||
import libs.home_assistant
|
||||
import libs.panel_cmd
|
||||
import yaml
|
||||
from panel import LovelaceUIPanel
|
||||
import os
|
||||
import threading
|
||||
from watchdog.events import FileSystemEventHandler
|
||||
from watchdog.observers import Observer
|
||||
import signal
|
||||
import sys
|
||||
from queue import Queue
|
||||
from mqtt import MqttManager
|
||||
|
||||
logging.getLogger("watchdog").propagate = False
|
||||
|
||||
settings = {}
|
||||
panels = {}
|
||||
panel_in_queues = {}
|
||||
panel_out_queue = Queue(maxsize=20)
|
||||
last_settings_file_mtime = 0
|
||||
mqtt_connect_time = 0
|
||||
has_sent_reload_command = False
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
def on_ha_update(entity_id):
|
||||
global panel_in_queues
|
||||
# send HA updates to all panels
|
||||
for queue in panel_in_queues.values():
|
||||
queue.put(("HA:", entity_id))
|
||||
|
||||
def connect():
|
||||
global settings, panel_out_queue
|
||||
if settings["mqtt_server"] != "":
|
||||
MqttManager(settings, panel_out_queue, panel_in_queues)
|
||||
else:
|
||||
logging.info("MQTT values not configured, will not connect.")
|
||||
|
||||
# MQTT Connected, start APIs if configured
|
||||
if settings["home_assistant_address"] != "" and settings["home_assistant_token"] != "":
|
||||
libs.home_assistant.init(settings, on_ha_update)
|
||||
libs.home_assistant.connect()
|
||||
else:
|
||||
logging.info("Home Assistant values not configured, will not connect.")
|
||||
|
||||
def setup_panels():
|
||||
global settings, panel_in_queues
|
||||
# Create NsPanel object
|
||||
for name, settings_panel in settings["nspanels"].items():
|
||||
if "timeZone" not in settings_panel:
|
||||
settings_panel["timeZone"] = settings.get("timeZone", "Europe/Berlin")
|
||||
if "locale" not in settings_panel:
|
||||
settings_panel["timezone"] = settings.get("locale", "en_US")
|
||||
if "hiddenCards" not in settings_panel:
|
||||
settings_panel["hiddenCards"] = settings.get("hiddenCards", [])
|
||||
|
||||
msg_in_queue = Queue(maxsize=20)
|
||||
panel_in_queues[settings_panel["panelRecvTopic"]] = msg_in_queue
|
||||
panel_thread = threading.Thread(target=panel_thread_target, args=(msg_in_queue, name, settings_panel, panel_out_queue))
|
||||
panel_thread.daemon = True
|
||||
panel_thread.start()
|
||||
|
||||
def panel_thread_target(queue_in, name, settings_panel, queue_out):
|
||||
panel = LovelaceUIPanel(name, settings_panel, queue_out)
|
||||
while True:
|
||||
msg = queue_in.get()
|
||||
if msg[0] == "MQTT:":
|
||||
panel.customrecv_event_callback(msg[1])
|
||||
elif msg[0] == "HA:":
|
||||
panel.ha_event_callback(msg[1])
|
||||
|
||||
def get_config_file():
|
||||
CONFIG_FILE = os.getenv('CONFIG_FILE')
|
||||
if not CONFIG_FILE:
|
||||
CONFIG_FILE = './panels.yaml'
|
||||
return CONFIG_FILE
|
||||
|
||||
def get_config(file):
|
||||
global settings
|
||||
|
||||
try:
|
||||
with open(file, 'r', encoding="utf8") as file:
|
||||
settings = yaml.safe_load(file)
|
||||
except yaml.YAMLError as exc:
|
||||
print ("Error while parsing YAML file:")
|
||||
if hasattr(exc, 'problem_mark'):
|
||||
if exc.context != None:
|
||||
print (' parser says\n' + str(exc.problem_mark) + '\n ' +
|
||||
str(exc.problem) + ' ' + str(exc.context) +
|
||||
'\nPlease correct data and retry.')
|
||||
else:
|
||||
print (' parser says\n' + str(exc.problem_mark) + '\n ' +
|
||||
str(exc.problem) + '\nPlease correct data and retry.')
|
||||
else:
|
||||
print ("Something went wrong while parsing yaml file")
|
||||
return False
|
||||
|
||||
if not settings.get("mqtt_username"):
|
||||
settings["mqtt_username"] = os.getenv('MQTT_USER')
|
||||
if not settings.get("mqtt_password"):
|
||||
settings["mqtt_password"] = os.getenv('MQTT_PASS')
|
||||
if not settings.get("mqtt_port"):
|
||||
settings["mqtt_port"] = os.getenv('MQTT_PORT')
|
||||
if not settings.get("mqtt_server"):
|
||||
settings["mqtt_server"] = os.getenv('MQTT_SERVER')
|
||||
|
||||
|
||||
settings["is_addon"] = False
|
||||
|
||||
if not settings.get("home_assistant_token"):
|
||||
st = os.getenv('SUPERVISOR_TOKEN')
|
||||
if st and "home_assistant_token" not in settings and "home_assistant_address" not in settings:
|
||||
settings["home_assistant_token"] = st
|
||||
settings["home_assistant_address"] = "http://supervisor"
|
||||
settings["is_addon"] = True
|
||||
return True
|
||||
|
||||
def config_watch():
|
||||
class ConfigChangeEventHandler(FileSystemEventHandler):
|
||||
def __init__(self, base_paths):
|
||||
self.base_paths = base_paths
|
||||
|
||||
def dispatch(self, event):
|
||||
for base_path in self.base_paths:
|
||||
if event.src_path.endswith(base_path):
|
||||
super(ConfigChangeEventHandler, self).dispatch(event)
|
||||
return
|
||||
|
||||
def on_modified(self, event):
|
||||
logging.info('Modification detected. Reloading panels.')
|
||||
pid = os.getpid()
|
||||
os.kill(pid, signal.SIGTERM)
|
||||
|
||||
logging.info('Watching for changes in config file')
|
||||
project_files = []
|
||||
project_files.append(get_config_file())
|
||||
handler = ConfigChangeEventHandler(project_files)
|
||||
observer = Observer()
|
||||
observer.schedule(handler, path=os.path.dirname(get_config_file()), recursive=True)
|
||||
observer.start()
|
||||
while True:
|
||||
time.sleep(1)
|
||||
|
||||
def signal_handler(signum, frame):
|
||||
logging.info(f"Received signal {signum}. Initiating restart...")
|
||||
python = sys.executable
|
||||
os.execl(python, python, *sys.argv)
|
||||
|
||||
if __name__ == '__main__':
|
||||
signal.signal(signal.SIGTERM, signal_handler)
|
||||
threading.Thread(target=config_watch).start()
|
||||
if (get_config(get_config_file())):
|
||||
connect()
|
||||
setup_panels()
|
||||
else:
|
||||
while True:
|
||||
time.sleep(100)
|
||||
68
nspanel-lovelace-ui/rootfs/usr/bin/mqtt-manager/mqtt.py
Normal file
68
nspanel-lovelace-ui/rootfs/usr/bin/mqtt-manager/mqtt.py
Normal file
@@ -0,0 +1,68 @@
|
||||
from uuid import getnode as get_mac
|
||||
import paho.mqtt.client as mqtt
|
||||
import logging
|
||||
import time
|
||||
import json
|
||||
import threading
|
||||
|
||||
|
||||
class MqttManager:
|
||||
def __init__(self, settings, msg_in_queue, msg_out_queue_list):
|
||||
mqtt_client_name = "NSPanelLovelaceManager_" + str(get_mac())
|
||||
self.client = mqtt.Client(mqtt_client_name)
|
||||
self.msg_in_queue = msg_in_queue
|
||||
self.msg_out_queue_list = msg_out_queue_list
|
||||
self.settings = settings
|
||||
|
||||
self.client.on_connect = self.on_mqtt_connect
|
||||
self.client.on_message = self.on_mqtt_message
|
||||
self.client.username_pw_set(
|
||||
settings["mqtt_username"], settings["mqtt_password"])
|
||||
# Wait for connection
|
||||
connection_return_code = 0
|
||||
mqtt_server = settings["mqtt_server"]
|
||||
mqtt_port = int(settings["mqtt_port"])
|
||||
logging.info("Connecting to %s:%i as %s",
|
||||
mqtt_server, mqtt_port, mqtt_client_name)
|
||||
while True:
|
||||
try:
|
||||
self.client.connect(mqtt_server, mqtt_port, 5)
|
||||
break # Connection call did not raise exception, connection is sucessfull
|
||||
except: # pylint: disable=bare-except
|
||||
logging.exception(
|
||||
"Failed to connect to MQTT %s:%i. Will try again in 10 seconds. Code: %s", mqtt_server, mqtt_port, connection_return_code)
|
||||
time.sleep(10.)
|
||||
self.client.loop_start()
|
||||
process_thread = threading.Thread(target=self.process_in_queue, args=(self.client, self.msg_in_queue))
|
||||
process_thread.daemon = True
|
||||
process_thread.start()
|
||||
|
||||
def on_mqtt_connect(self, client, userdata, flags, rc):
|
||||
logging.info("Connected to MQTT Server")
|
||||
# subscribe to panelRecvTopic of each panel
|
||||
for settings_panel in self.settings["nspanels"].values():
|
||||
client.subscribe(settings_panel["panelRecvTopic"])
|
||||
|
||||
def on_mqtt_message(self, client, userdata, msg):
|
||||
try:
|
||||
if msg.payload.decode() == "":
|
||||
return
|
||||
if msg.topic in self.msg_out_queue_list.keys():
|
||||
data = json.loads(msg.payload.decode('utf-8'))
|
||||
if "CustomRecv" in data:
|
||||
queue = self.msg_out_queue_list[msg.topic]
|
||||
queue.put(("MQTT:", data["CustomRecv"]))
|
||||
else:
|
||||
logging.debug("Received unhandled message on topic: %s", msg.topic)
|
||||
except Exception: # pylint: disable=broad-exception-caught
|
||||
logging.exception("Something went wrong during processing of message:")
|
||||
try:
|
||||
logging.error(msg.payload.decode('utf-8'))
|
||||
except: # pylint: disable=bare-except
|
||||
logging.error(
|
||||
"Something went wrong when processing the exception message, couldn't decode payload to utf-8.")
|
||||
|
||||
def process_in_queue(self, client, msg_in_queue):
|
||||
while True:
|
||||
msg = msg_in_queue.get()
|
||||
client.publish(msg[0], msg[1])
|
||||
@@ -12,14 +12,14 @@ import ha_control
|
||||
|
||||
class LovelaceUIPanel:
|
||||
|
||||
def __init__(self, name_panel, settings_panel):
|
||||
def __init__(self, name_panel, settings_panel, msg_out_queue):
|
||||
self.name = name_panel
|
||||
self.settings = settings_panel
|
||||
self.msg_out_queue = msg_out_queue
|
||||
self.sendTopic = self.settings["panelSendTopic"]
|
||||
self.recvTopic = self.settings["panelRecvTopic"]
|
||||
self.model = self.settings.get("model", "eu")
|
||||
|
||||
|
||||
self.current_card = None
|
||||
self.privious_cards = []
|
||||
self.cards = {}
|
||||
@@ -94,7 +94,7 @@ class LovelaceUIPanel:
|
||||
for e in self.screensaver.entities:
|
||||
e.prerender()
|
||||
|
||||
libs.panel_cmd.page_type(self.sendTopic, "pageStartup")
|
||||
libs.panel_cmd.page_type(self.msg_out_queue, self.sendTopic, "pageStartup")
|
||||
|
||||
|
||||
def schedule_thread_target(self):
|
||||
@@ -106,13 +106,13 @@ class LovelaceUIPanel:
|
||||
use_timezone = tz.gettz(self.settings["timeZone"])
|
||||
time_string = datetime.datetime.now(
|
||||
use_timezone).strftime(self.settings["timeFormat"])
|
||||
libs.panel_cmd.send_time(self.sendTopic, time_string)
|
||||
libs.panel_cmd.send_time(self.msg_out_queue, self.sendTopic, time_string)
|
||||
|
||||
def update_date(self):
|
||||
dateformat = self.settings["dateFormat"]
|
||||
date_string = babel.dates.format_date(
|
||||
datetime.datetime.now(), dateformat, locale=self.settings["locale"])
|
||||
libs.panel_cmd.send_date(self.sendTopic, date_string)
|
||||
libs.panel_cmd.send_date(self.msg_out_queue, self.sendTopic, date_string)
|
||||
|
||||
def searchCard(self, iid):
|
||||
if iid in self.navigate_keys:
|
||||
@@ -140,9 +140,9 @@ class LovelaceUIPanel:
|
||||
if etype=="light":
|
||||
effectList = e.config.get("effectList")
|
||||
if etype in ['input_select', 'media_player']:
|
||||
libs.panel_cmd.entityUpdateDetail2(self.sendTopic, detail_open(self.settings["locale"], etype, entity_id, entity_id_iid, sendTopic=self.sendTopic, options_list=effectList))
|
||||
libs.panel_cmd.entityUpdateDetail2(self.msg_out_queue, self.sendTopic, detail_open(self.settings["locale"], etype, entity_id, entity_id_iid, self.msg_out_queue, sendTopic=self.sendTopic, options_list=effectList))
|
||||
else:
|
||||
libs.panel_cmd.entityUpdateDetail(self.sendTopic, detail_open(self.settings["locale"], etype, entity_id, entity_id_iid, sendTopic=self.sendTopic, options_list=effectList))
|
||||
libs.panel_cmd.entityUpdateDetail(self.msg_out_queue, self.sendTopic, detail_open(self.settings["locale"], etype, entity_id, entity_id_iid, self.msg_out_queue, sendTopic=self.sendTopic, options_list=effectList))
|
||||
|
||||
involved_entities = ha_control.calculate_dim_values(
|
||||
self.settings.get("sleepTracking"),
|
||||
@@ -160,15 +160,9 @@ class LovelaceUIPanel:
|
||||
if not self.current_card:
|
||||
return
|
||||
if switchPages:
|
||||
libs.panel_cmd.page_type(self.sendTopic, self.current_card.type)
|
||||
libs.panel_cmd.page_type(self.msg_out_queue, self.sendTopic, self.current_card.type)
|
||||
if requested:
|
||||
self.current_card.render()
|
||||
# send sleepTimeout
|
||||
#sleepTimeout = self.settings.get("sleepTimeout", 20)
|
||||
#if self.current_card.config.get("sleepTimeout"):
|
||||
# sleepTimeout = self.current_card.config.get("sleepTimeout")
|
||||
#libs.panel_cmd.timeout(self.sendTopic, sleepTimeout)
|
||||
#self.dimmode()
|
||||
|
||||
def dimmode(self):
|
||||
# send dimmode
|
||||
@@ -187,7 +181,7 @@ class LovelaceUIPanel:
|
||||
backgroundColor = 0
|
||||
fontColor = ""
|
||||
featExperimentalSliders = self.settings.get("featExperimentalSliders", 0)
|
||||
libs.panel_cmd.dimmode(self.sendTopic, dimValue, dimValueNormal, backgroundColor, fontColor, featExperimentalSliders)
|
||||
libs.panel_cmd.dimmode(self.msg_out_queue, self.sendTopic, dimValue, dimValueNormal, backgroundColor, fontColor, featExperimentalSliders)
|
||||
|
||||
def get_default_card(self):
|
||||
defaultCard = self.settings.get("defaultCard")
|
||||
@@ -214,7 +208,7 @@ class LovelaceUIPanel:
|
||||
sleepTimeout = self.settings.get("sleepTimeout", 20)
|
||||
if self.current_card.config.get("sleepTimeout"):
|
||||
sleepTimeout = self.current_card.config.get("sleepTimeout")
|
||||
libs.panel_cmd.timeout(self.sendTopic, sleepTimeout)
|
||||
libs.panel_cmd.timeout(self.msg_out_queue, self.sendTopic, sleepTimeout)
|
||||
self.dimmode()
|
||||
|
||||
if msg[1] == "sleepReached":
|
||||
@@ -288,8 +282,8 @@ class LovelaceUIPanel:
|
||||
if entity_id.startswith("light"):
|
||||
effectList = e.config.get("effectList")
|
||||
if entity_id.split(".")[0] in ['input_select', 'media_player']:
|
||||
libs.panel_cmd.entityUpdateDetail2(self.sendTopic, detail_open(self.settings["locale"], msg[2], entity_id, msg[3], sendTopic=self.sendTopic, options_list=effectList))
|
||||
libs.panel_cmd.entityUpdateDetail2(self.msg_out_queue, self.sendTopic, detail_open(self.settings["locale"], msg[2], entity_id, msg[3], self.msg_out_queue, sendTopic=self.sendTopic, options_list=effectList))
|
||||
else:
|
||||
libs.panel_cmd.entityUpdateDetail(self.sendTopic, detail_open(self.settings["locale"], msg[2], entity_id, msg[3], sendTopic=self.sendTopic))
|
||||
libs.panel_cmd.entityUpdateDetail(self.msg_out_queue, self.sendTopic, detail_open(self.settings["locale"], msg[2], entity_id, msg[3], self.msg_out_queue, sendTopic=self.sendTopic))
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user