Skip to content

Commit

Permalink
Added workaround to issue where the MQTTManager would sometimes remov…
Browse files Browse the repository at this point in the history
…e the online status from a panel, then restart to get the new panel and after that fail to see the online message (as it was previously removed)
  • Loading branch information
tpanajott committed Jul 27, 2023
1 parent ebf7e58 commit fa4d2f7
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 61 deletions.
39 changes: 22 additions & 17 deletions docker/web/mqtt_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,17 @@ def get_machine_mac():

settings = {}
last_settings_file_mtime = 0
mqtt_connect_time = 0
has_sent_reload_command = False
client = mqtt.Client("NSPanelManager_" + get_machine_mac())
logging.basicConfig(level=logging.DEBUG)
logging.getLogger("urllib3").propagate = False
last_sent_time_string = ""

def millis():
return round(time.time() * 1000)


def get_md5_sum(url):
req = get(url)
if req.status_code == 200:
Expand Down Expand Up @@ -86,11 +91,7 @@ async def send_mqttmanager_status(websocket = None):
await websocket.send(json.dumps(status))

def send_mqttmanager_status_sync():
logging.debug("Sending MQTTManager status from sync.")
asyncio.run(send_mqttmanager_status())
# loop = asyncio.get_event_loop()
# coroutine = send_mqttmanager_status()
# loop.run_until_complete(coroutine)

def on_connect(client, userdata, flags, rc):
logging.info("Connected to MQTT Server")
Expand Down Expand Up @@ -126,15 +127,21 @@ async def on_websocket_message(websocket, message):
if nspanel_id in mqtt_manager_libs.nspanel_states.states:
send_nspanel_command(nspanel_id, {"command": "reboot"})
if "address" in mqtt_manager_libs.nspanel_states.states[nspanel_id]: # TODO: Remove old HTTP GET method after a few updates as it is not longed in us.
get("http://" + mqtt_manager_libs.nspanel_states.states[nspanel_id]["address"] + "/do_reboot")
try:
get("http://" + mqtt_manager_libs.nspanel_states.states[nspanel_id]["address"] + "/do_reboot")
except:
pass
elif message["command"] == "firmware_update_nspanels":
if "nspanels" in message["args"]:
for nspanel_id in message["args"]["nspanels"]:
nspanel_id = int(nspanel_id)
if nspanel_id in mqtt_manager_libs.nspanel_states.states:
send_nspanel_command(nspanel_id, {"command": "firmware_update"})
if "address" in mqtt_manager_libs.nspanel_states.states[nspanel_id]: # TODO: Remove old HTTP GET method after a few updates as it is not longed in us.
post("http://" + mqtt_manager_libs.nspanel_states.states[nspanel_id]["address"] + "/start_ota_update")
try:
post("http://" + mqtt_manager_libs.nspanel_states.states[nspanel_id]["address"] + "/start_ota_update")
except:
pass

elif message["command"] == "tft_update_nspanels":
if "nspanels" in message["args"]:
Expand All @@ -143,7 +150,10 @@ async def on_websocket_message(websocket, message):
if nspanel_id in mqtt_manager_libs.nspanel_states.states:
send_nspanel_command(nspanel_id, {"command": "tft_update"})
if "address" in mqtt_manager_libs.nspanel_states.states[nspanel_id]: # TODO: Remove old HTTP GET method after a few updates as it is not longed in us.
post("http://" + mqtt_manager_libs.nspanel_states.states[nspanel_id]["address"] + "/start_tft_ota_update")
try:
post("http://" + mqtt_manager_libs.nspanel_states.states[nspanel_id]["address"] + "/start_tft_ota_update")
except:
pass

await websocket.send(json.dumps(reply))

Expand All @@ -153,7 +163,6 @@ def on_message(client, userdata, msg):
if msg.payload.decode() == "":
return
parts = msg.topic.split('/')
# Messages received was a status update (online/offline)
if parts[-1] == "log":
message_parts = msg.payload.decode('utf-8').split(';')
data = {
Expand All @@ -166,33 +175,33 @@ def on_message(client, userdata, msg):
}
mqtt_manager_libs.websocket_server.send_message(json.dumps(data))
elif parts[-1] == "status":
# Messages received was a status update (online/offline)
panel_found = False
for panel in settings["nspanels"].values():
if panel["name"] == parts[-2]:
panel_found = True
break

if panel_found:
logging.debug("Got status from existing panel.")
panel = parts[1]
data = json.loads(msg.payload.decode('utf-8'))
panel_id = mqtt_manager_libs.nspanel_states.get_id_by_mac(data["mac"])
if panel_id >= 0:
send_online_status(panel, data)
mqtt_manager_libs.nspanel_states.states[panel_id].update(data)
ws_data = {
"type": "status",
"payload": mqtt_manager_libs.nspanel_states.states[panel_id]
}
mqtt_manager_libs.websocket_server.send_message(json.dumps(ws_data))
else:
elif mqtt_connect_time + 30000 <= millis():
logging.warning(F"Removing mqtt topic: {msg.topic} as panel does not exist any more.")
client.publish('/'.join(parts), payload=None, qos=0, retain=True)
elif parts[-1] == "status_report":
panel = parts[1]
data = json.loads(msg.payload.decode('utf-8'))
panel_id = mqtt_manager_libs.nspanel_states.get_id_by_mac(data["mac"])
if panel_id >= 0:
send_online_status(panel, data)
mqtt_manager_libs.nspanel_states.states[panel_id].update(data)
ws_data = {
"type": "status",
Expand Down Expand Up @@ -232,7 +241,7 @@ def on_message(client, userdata, msg):
mqtt_manager_libs.scenes.save_scene(None, parts[3]) # Save scene were part[3] is scene name
elif msg.topic.startswith("nspanel/entities/light/"):
light_id =int(parts[3])
if not light_id in mqtt_manager_libs.light_states.states:
if not light_id in mqtt_manager_libs.light_states.states and mqtt_connect_time + 10000 <= millis():
logging.warning(F"Removing MQTT topic '{msg.topic}' for light that does not exist any more.")
client.publish('/'.join(parts), payload=None, qos=0, retain=True)
else:
Expand All @@ -250,11 +259,6 @@ def send_status_report(panel, new_status):
post("http://127.0.0.1:8000/api/set_panel_status/" + new_status["mac"] + "/", json=new_status)


def send_online_status(panel, new_status):
pass
#post("http://127.0.0.1:8000/api/set_panel_online_status/" + new_status["mac"] + "/", json=new_status)


def get_config():
global settings
while True:
Expand Down Expand Up @@ -310,6 +314,7 @@ def connect_and_loop():
except:
logging.exception(F"Failed to connect to MQTT {mqtt_server}:{mqtt_port}. Will try again in 10 seconds. Code: {connection_return_code}")
time.sleep(10)
mqtt_connect_time = millis()

# Send reload command to panels for them to reload config as MQTT manager JUST restarted (probably because of config change)
if has_sent_reload_command == False:
Expand Down
88 changes: 46 additions & 42 deletions docker/web/nspanelmanager/web/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,48 +269,52 @@ def delete_panel(request, panel_id: int):


def get_nspanel_config(request):
nspanel = NSPanel.objects.get(mac_address=request.GET["mac"])
base = {}
base["name"] = nspanel.friendly_name
base["home"] = nspanel.room.id
base["raise_to_100_light_level"] = get_setting_with_default(
"raise_to_100_light_level", 95)
base["color_temp_min"] = get_setting_with_default("color_temp_min", 2000)
base["color_temp_max"] = get_setting_with_default("color_temp_max", 6000)
base["reverse_color_temp"] = get_setting_with_default("reverse_color_temp", False)
base["min_button_push_time"] = get_setting_with_default("min_button_push_time", 50)
base["button_long_press_time"] = get_setting_with_default("button_long_press_time", 5000)
base["special_mode_trigger_time"] = get_setting_with_default("special_mode_trigger_time", 300)
base["special_mode_release_time"] = get_setting_with_default("special_mode_release_time", 5000)
base["mqtt_ignore_time"] = get_setting_with_default("mqtt_ignore_time", 3000)
base["screen_dim_level"] = get_nspanel_setting_with_default(nspanel.id, "screen_dim_level", get_setting_with_default("screen_dim_level", 100))
base["screensaver_dim_level"] = get_nspanel_setting_with_default(nspanel.id, "screensaver_dim_level", get_setting_with_default("screensaver_dim_level", 0))
base["screensaver_activation_timeout"] = get_nspanel_setting_with_default(nspanel.id, "screensaver_activation_timeout", get_setting_with_default("screensaver_activation_timeout", 30000))
base["show_screensaver_clock"] = get_nspanel_setting_with_default(nspanel.id, "show_screensaver_clock", get_setting_with_default("show_screensaver_clock", False))
base["clock_us_style"] = get_setting_with_default("clock_us_style", False)
base["button1_mode"] = nspanel.button1_mode
base["use_farenheit"] = get_setting_with_default("use_farenheit", False)
base["lock_to_default_room"] = get_nspanel_setting_with_default(nspanel.id, "lock_to_default_room", "False")
base["relay1_default_mode"] = get_nspanel_setting_with_default(nspanel.id, "relay1_default_mode", False)
base["relay2_default_mode"] = get_nspanel_setting_with_default(nspanel.id, "relay2_default_mode", False)
base["temperature_calibration"] = float(get_nspanel_setting_with_default(nspanel.id, "temperature_calibration", 0))
if nspanel.button1_detached_mode_light:
base["button1_detached_light"] = nspanel.button1_detached_mode_light.id
else:
base["button1_detached_mode_light"] = -1
base["button2_mode"] = nspanel.button2_mode
if nspanel.button2_detached_mode_light:
base["button2_detached_light"] = nspanel.button2_detached_mode_light.id
else:
base["button2_detached_light"] = -1
base["rooms"] = []
for room in Room.objects.all().order_by('displayOrder'):
base["rooms"].append(room.id)
base["scenes"] = {}
for scene in Scene.objects.filter(room__isnull=True):
base["scenes"][scene.id] = {}
base["scenes"][scene.id]["name"] = scene.friendly_name
return JsonResponse(base)
try:
nspanel = NSPanel.objects.get(mac_address=request.GET["mac"])
base = {}
base["name"] = nspanel.friendly_name
base["home"] = nspanel.room.id
base["raise_to_100_light_level"] = get_setting_with_default(
"raise_to_100_light_level", 95)
base["color_temp_min"] = get_setting_with_default("color_temp_min", 2000)
base["color_temp_max"] = get_setting_with_default("color_temp_max", 6000)
base["reverse_color_temp"] = get_setting_with_default("reverse_color_temp", False)
base["min_button_push_time"] = get_setting_with_default("min_button_push_time", 50)
base["button_long_press_time"] = get_setting_with_default("button_long_press_time", 5000)
base["special_mode_trigger_time"] = get_setting_with_default("special_mode_trigger_time", 300)
base["special_mode_release_time"] = get_setting_with_default("special_mode_release_time", 5000)
base["mqtt_ignore_time"] = get_setting_with_default("mqtt_ignore_time", 3000)
base["screen_dim_level"] = get_nspanel_setting_with_default(nspanel.id, "screen_dim_level", get_setting_with_default("screen_dim_level", 100))
base["screensaver_dim_level"] = get_nspanel_setting_with_default(nspanel.id, "screensaver_dim_level", get_setting_with_default("screensaver_dim_level", 0))
base["screensaver_activation_timeout"] = get_nspanel_setting_with_default(nspanel.id, "screensaver_activation_timeout", get_setting_with_default("screensaver_activation_timeout", 30000))
base["show_screensaver_clock"] = get_nspanel_setting_with_default(nspanel.id, "show_screensaver_clock", get_setting_with_default("show_screensaver_clock", False))
base["clock_us_style"] = get_setting_with_default("clock_us_style", False)
base["button1_mode"] = nspanel.button1_mode
base["use_farenheit"] = get_setting_with_default("use_farenheit", False)
base["lock_to_default_room"] = get_nspanel_setting_with_default(nspanel.id, "lock_to_default_room", "False")
base["relay1_default_mode"] = get_nspanel_setting_with_default(nspanel.id, "relay1_default_mode", False)
base["relay2_default_mode"] = get_nspanel_setting_with_default(nspanel.id, "relay2_default_mode", False)
base["temperature_calibration"] = float(get_nspanel_setting_with_default(nspanel.id, "temperature_calibration", 0))
if nspanel.button1_detached_mode_light:
base["button1_detached_light"] = nspanel.button1_detached_mode_light.id
else:
base["button1_detached_mode_light"] = -1
base["button2_mode"] = nspanel.button2_mode
if nspanel.button2_detached_mode_light:
base["button2_detached_light"] = nspanel.button2_detached_mode_light.id
else:
base["button2_detached_light"] = -1
base["rooms"] = []
for room in Room.objects.all().order_by('displayOrder'):
base["rooms"].append(room.id)
base["scenes"] = {}
for scene in Scene.objects.filter(room__isnull=True):
base["scenes"][scene.id] = {}
base["scenes"][scene.id]["name"] = scene.friendly_name
return JsonResponse(base)
except:
print("Tried to get NSPanel config for panel that was not registered.")
return HttpResponse("", status=500)


def get_room_config(request, room_id: int):
Expand Down
4 changes: 2 additions & 2 deletions docker/web/nspanelmanager/web/templates/settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ <h5 class="title is-5">Global NSPanel settings</h5>
</div>
</div>
<div class="field">
<label class="label">MQTT ignore time<span class="question">
<label class="label">MQTT ignore time(ms)<span class="question">
<span class="icon question_icon">
<i class="mdi mdi-help"></i>
</span>
Expand Down Expand Up @@ -247,7 +247,7 @@ <h5 class="title is-5">Global NSPanel settings</h5>
</div>
</div>
<div class="field">
<label class="label">Screensaver activation timeout<span class="question">
<label class="label">Screensaver activation timeout(ms)<span class="question">
<span class="icon question_icon">
<i class="mdi mdi-help"></i>
</span>
Expand Down

0 comments on commit fa4d2f7

Please sign in to comment.