diff --git a/HMI files/nspmanager.HMI b/HMI files/nspmanager.HMI index dac79fa3..3a1c3e6e 100644 Binary files a/HMI files/nspmanager.HMI and b/HMI files/nspmanager.HMI differ diff --git a/HMI files/nspmanager.tft b/HMI files/nspmanager.tft index 3d2aadbd..5bc20110 100644 Binary files a/HMI files/nspmanager.tft and b/HMI files/nspmanager.tft differ diff --git a/README.md b/README.md index f0a78f26..c1a883ba 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Logging from NSPanels are done over MQTT to the topic `nspanel//log` |nspanel/panel_name/r1_state| 1 or 0 |Current relay 1 state, 1 or 0| |nspanel/panel_name/r2_cmd| 1 or 0 |Control relay 2 state, 1 or 0| |nspanel/panel_name/r2_state| 1 or 0 |Current relay 2 state, 1 or 0| +|nspanel/panel_name/temperature_state| temperature reading |Current temperature reading| # Currently working (but might require more work) * Integration with Home Assistant and OpenHAB diff --git a/docker/web/mqtt_manager.py b/docker/web/mqtt_manager.py index da37a1e0..e6f9cf24 100755 --- a/docker/web/mqtt_manager.py +++ b/docker/web/mqtt_manager.py @@ -70,13 +70,14 @@ def on_message(client, userdata, msg): parts = msg.topic.split('/') # Messages received was a status update (online/offline) if parts[-1] == "log": - message_parts = msg.payload.decode('utf-8').split(':') + message_parts = msg.payload.decode('utf-8').split(';') data = { "type": "log", "time": datetime.datetime.now().strftime("%H:%M:%S"), "panel": parts[1], - "level": message_parts[0], - "message": ':'.join(message_parts[1:]) + "mac": message_parts[0], + "level": message_parts[1], + "message": ':'.join(message_parts[2:]) } mqtt_manager_libs.websocket_server.send_message(json.dumps(data)) elif parts[-1] == "status": @@ -87,8 +88,7 @@ def on_message(client, userdata, msg): "type": "status", "payload": data } - mqtt_manager_libs.websocket_server.send_message( - json.dumps(ws_data)) + mqtt_manager_libs.websocket_server.send_message(json.dumps(ws_data)) elif parts[-1] == "status_report": panel = parts[1] data = json.loads(msg.payload.decode('utf-8')) @@ -97,8 +97,7 @@ def on_message(client, userdata, msg): "type": "status_report", "payload": data } - mqtt_manager_libs.websocket_server.send_message( - json.dumps(ws_data)) + mqtt_manager_libs.websocket_server.send_message(json.dumps(ws_data)) elif msg.topic == "nspanel/mqttmanager/command": data = json.loads(msg.payload.decode('utf-8')) # Verify that the mac_origin is off a panel that is controlled by this instance diff --git a/docker/web/mqtt_manager_libs/light.py b/docker/web/mqtt_manager_libs/light.py index 0fb8bc3e..9f124932 100644 --- a/docker/web/mqtt_manager_libs/light.py +++ b/docker/web/mqtt_manager_libs/light.py @@ -75,10 +75,12 @@ def set_light_level(self, light_level: int): logging.info(F"Sending kelvin update to OpenHAB for light {self.friendly_name}. Light was off previously.") mqtt_manager_libs.openhab.set_entity_color_temp(self.openhab_item_color_temp, self.color_temp) self.light_level = int(light_level) + self.last_mode_change = time.time()*1000 elif self.last_command_sent == "rgb": logging.info(F"Sending light update to OpenHAB for light {self.friendly_name}. Sending RGB value: {light_level}") if mqtt_manager_libs.openhab.set_entity_color_saturation(self.openhab_item_rgb, int(light_level), self.color_saturation, self.color_hue): self.light_level = int(light_level) + self.last_mode_change = time.time()*1000 else: logging.error("Unknown OpenHAB light state. Last command sent: " + self.last_command_sent) diff --git a/docker/web/mqtt_manager_libs/openhab.py b/docker/web/mqtt_manager_libs/openhab.py index e8094f62..e80e07ec 100644 --- a/docker/web/mqtt_manager_libs/openhab.py +++ b/docker/web/mqtt_manager_libs/openhab.py @@ -13,6 +13,9 @@ stop_keepalive = False settings = {} +def millis(): + return round(time() * 1000) + def init(settings_from_manager, mqtt_client_from_manager): global openhab_url, openhab_token, settings, mqtt_client @@ -38,7 +41,7 @@ def on_message(ws, message): for light in mqtt_manager_libs.light_states.states.values(): try: if light.type == "openhab": - current_time_ms = time()*1000 + current_time_ms = millis() if light.openhab_control_mode == "dimmer" and item == light.openhab_item_name: light_level_pct = int(float(payload["value"])) mqtt_client.publish(F"nspanel/entities/light/{light.id}/state_brightness_pct", light_level_pct, retain=True) @@ -61,6 +64,7 @@ def on_message(ws, message): mqtt_client.publish(F"nspanel/entities/light/{light.id}/state_kelvin", send_color_temp, retain=True) light.color_temp = send_color_temp light.last_command_sent = "color_temp" + light.last_mode_change = current_time_ms return None elif item == light.openhab_item_rgb and (current_time_ms >= light.last_mode_change + 1000 or light.last_command_sent == "rgb"): #hue, sat, brightness = payload["value"] @@ -74,9 +78,10 @@ def on_message(ws, message): mqtt_client.publish(F"nspanel/entities/light/{light.id}/state_sat", light.color_saturation, retain=True) mqtt_client.publish(F"nspanel/entities/light/{light.id}/state_brightness_pct", light.light_level, retain=True) light.last_command_sent = "rgb" + light.last_mode_change = current_time_ms return None - - logging.info(F"Got state update event for light not managed by NSPanelManager. Topic: " + json_msg["topic"]) + else: + logging.info(F"Got state update event for light not managed by NSPanelManager. Message: {message}") except Exception as e: traceback.print_exc() @@ -156,7 +161,7 @@ def set_entity_brightness(openhab_item_name: str, openhab_control_mode: str, lig # Format OpenHAB state update if light_level > 0: onoff = "ON" - if light_level <= 0: + elif light_level <= 0: onoff = "OFF" msg = { diff --git a/docker/web/nspanelmanager/data_file.bin b/docker/web/nspanelmanager/data_file.bin index 3a95f5a0..7d11de1f 100644 Binary files a/docker/web/nspanelmanager/data_file.bin and b/docker/web/nspanelmanager/data_file.bin differ diff --git a/docker/web/nspanelmanager/firmware.bin b/docker/web/nspanelmanager/firmware.bin index 68e921b8..9d76e9a5 100644 Binary files a/docker/web/nspanelmanager/firmware.bin and b/docker/web/nspanelmanager/firmware.bin differ diff --git a/docker/web/nspanelmanager/gui.tft b/docker/web/nspanelmanager/gui.tft index 3d2aadbd..5bc20110 100644 Binary files a/docker/web/nspanelmanager/gui.tft and b/docker/web/nspanelmanager/gui.tft differ diff --git a/docker/web/nspanelmanager/web/api.py b/docker/web/nspanelmanager/web/api.py index df127e72..2b8184e8 100644 --- a/docker/web/nspanelmanager/web/api.py +++ b/docker/web/nspanelmanager/web/api.py @@ -54,6 +54,7 @@ def get_mqtt_manager_config(request): "openhab_color_temp_channel_name", "") return_json["openhab_rgb_channel_name"] = get_setting_with_default("openhab_rgb_channel_name", "") return_json["clock_us_style"] = get_setting_with_default("clock_us_style", False) + return_json["use_farenheit"] = get_setting_with_default("use_farenheit", False) return_json["lights"] = {} for light in Light.objects.all(): @@ -159,9 +160,11 @@ def register_nspanel(request): """Update the already existing NSPanel OR create a new one""" data = json.loads(request.body) new_panel = NSPanel.objects.filter(mac_address=data['mac_address']).first() + panel_already_exists = True if not new_panel: new_panel = NSPanel() + panel_already_exists = False new_panel.friendly_name = data['friendly_name'] new_panel.mac_address = data['mac_address'] @@ -178,7 +181,8 @@ def register_nspanel(request): # Save the update/Create new panel new_panel.save() - restart_mqtt_manager() + if not panel_already_exists: + restart_mqtt_manager() return HttpResponse('OK', status=200) @@ -207,6 +211,7 @@ def get_nspanel_config(request): base["clock_us_style"] = get_setting_with_default("clock_us_style", False) base["screensaver_activation_timeout"] = get_setting_with_default("screensaver_activation_timeout", 30000) base["button1_mode"] = nspanel.button1_mode + base["use_farenheit"] = get_setting_with_default("use_farenheit", False) if nspanel.button1_detached_mode_light: base["button1_detached_light"] = nspanel.button1_detached_mode_light.id else: @@ -278,8 +283,10 @@ def set_panel_status(request, panel_mac: str): nspanel = nspanels.first() # We got a match json_payload = json.loads(request.body.decode('utf-8')) + print(json_payload); nspanel.wifi_rssi = int(json_payload["rssi"]) nspanel.heap_used_pct = int(json_payload["heap_used_pct"]) + nspanel.temperature = round(json_payload["temperature"], 2) nspanel.save() return HttpResponse("", status=200) diff --git a/docker/web/nspanelmanager/web/migrations/0032_nspanel_temperature.py b/docker/web/nspanelmanager/web/migrations/0032_nspanel_temperature.py new file mode 100644 index 00000000..35558f47 --- /dev/null +++ b/docker/web/nspanelmanager/web/migrations/0032_nspanel_temperature.py @@ -0,0 +1,18 @@ +# Generated by Django 4.1.7 on 2023-06-23 22:43 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('web', '0031_lightstate_color_temperature'), + ] + + operations = [ + migrations.AddField( + model_name='nspanel', + name='temperature', + field=models.FloatField(default=0), + ), + ] diff --git a/docker/web/nspanelmanager/web/models.py b/docker/web/nspanelmanager/web/models.py index 86a79c65..364495b9 100755 --- a/docker/web/nspanelmanager/web/models.py +++ b/docker/web/nspanelmanager/web/models.py @@ -41,6 +41,7 @@ class NSPanel(models.Model): room = models.ForeignKey(Room, on_delete=models.CASCADE) wifi_rssi = models.IntegerField(default=0) heap_used_pct = models.IntegerField(default=0) + temperature = models.FloatField(default=0) online_state = models.BooleanField(default=False) button1_mode = models.IntegerField(default=0) button1_detached_mode_light = models.ForeignKey("Light", on_delete=models.SET_NULL, blank=True, null=True, related_name="+") diff --git a/docker/web/nspanelmanager/web/static/edit_nspanel.js b/docker/web/nspanelmanager/web/static/edit_nspanel.js index 0d0ce0fa..292bf157 100644 --- a/docker/web/nspanelmanager/web/static/edit_nspanel.js +++ b/docker/web/nspanelmanager/web/static/edit_nspanel.js @@ -3,7 +3,7 @@ function connect_to_websocket() { webSocket.onmessage = (event) => { data = JSON.parse(event.data); - if(data.type == "log") { + if(data.type == "log" && data.mac == $('#nspanel_mac').text()) { var add_html = ""; add_html += data.time; add_html += ""; diff --git a/docker/web/nspanelmanager/web/static/index.js b/docker/web/nspanelmanager/web/static/index.js index 506a096b..05ffcb62 100644 --- a/docker/web/nspanelmanager/web/static/index.js +++ b/docker/web/nspanelmanager/web/static/index.js @@ -15,6 +15,7 @@ function connect_to_websocket() { webSocket.onmessage = (event) => { data = JSON.parse(event.data); + console.log(data); if(data.type == "status") { let mac_selector = data.payload.mac; mac_selector = mac_selector.replaceAll(":", "\\:"); @@ -23,7 +24,7 @@ function connect_to_websocket() { // Got a state update for a panel that does not exist in this page, reload to show it if it has registered setTimeout(function() { location.reload(); - }, 5000); + }, 2000); } if(data.payload.state == "online") { @@ -37,6 +38,10 @@ function connect_to_websocket() { let mac_selector = data.payload.mac; mac_selector = mac_selector.replaceAll(":", "\\:"); $("#heap_used_" + mac_selector).text(data.payload.heap_used_pct + "%"); + var temperature_unit = $("#temperature_" + mac_selector).text().slice(-2); + $("#temperature_" + mac_selector).text(Math.round(data.payload.temperature*100)/100 + " " + temperature_unit); + + var new_rssi_classes = ""; if(data.payload.rssi <= -90) { new_rssi_classes = "mdi mdi-wifi-strength-1-alert"; @@ -83,10 +88,30 @@ function connect_to_websocket() { console.log("Websocket closed, trying in 1 second"); setTimeout(function() { connect_to_websocket(); + // location.reload(); }, 1000); }; } $(document).ready(function() { connect_to_websocket(); -}); \ No newline at end of file + + $("#firmware_upload_file_input").change(function (){ + var fileName = $(this).val().replace("C:\\fakepath\\", ""); + $("#firmware_upload_file_name").html(fileName); + }); + + + $("#data_upload_file_input").change(function (){ + var fileName = $(this).val().replace("C:\\fakepath\\", ""); + $("#data_upload_file_name").html(fileName); + }); + + + $("#tft_upload_file_input").change(function (){ + var fileName = $(this).val().replace("C:\\fakepath\\", ""); + $("#tft_upload_file_name").html(fileName); + }); +}); + + diff --git a/docker/web/nspanelmanager/web/templates/edit_nspanel.html b/docker/web/nspanelmanager/web/templates/edit_nspanel.html index 854acfcd..2df3bc4c 100644 --- a/docker/web/nspanelmanager/web/templates/edit_nspanel.html +++ b/docker/web/nspanelmanager/web/templates/edit_nspanel.html @@ -8,6 +8,7 @@ {% block content %}

{{ panel.friendly_name }} settings

+

MAC: {{ panel.mac_address }}

{% csrf_token %}
diff --git a/docker/web/nspanelmanager/web/templates/index.html b/docker/web/nspanelmanager/web/templates/index.html index 4f601494..12c12c44 100644 --- a/docker/web/nspanelmanager/web/templates/index.html +++ b/docker/web/nspanelmanager/web/templates/index.html @@ -15,8 +15,9 @@

Panels

Room IP Address Status - + + Version Actions @@ -53,6 +54,7 @@

Panels

{% endif %} {{ nspanel.heap_used_pct }}% + {{ nspanel.temperature }} {{ temperature_unit }} {{ nspanel.version }} Panels {% endfor %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/docker/web/nspanelmanager/web/templates/modals.html b/docker/web/nspanelmanager/web/templates/modals.html index 38314238..76ad221f 100644 --- a/docker/web/nspanelmanager/web/templates/modals.html +++ b/docker/web/nspanelmanager/web/templates/modals.html @@ -33,18 +33,19 @@
@@ -65,18 +66,19 @@
@@ -97,18 +99,19 @@
@@ -204,4 +207,4 @@
- \ No newline at end of file + diff --git a/docker/web/nspanelmanager/web/templates/settings.html b/docker/web/nspanelmanager/web/templates/settings.html index 8009afef..11afdd1c 100644 --- a/docker/web/nspanelmanager/web/templates/settings.html +++ b/docker/web/nspanelmanager/web/templates/settings.html @@ -12,6 +12,9 @@
NSPanel settings
+
+ Changes to NSPanel settings on this page requires a reboot of each panel to take effect. +
+
+ +
diff --git a/docker/web/nspanelmanager/web/views.py b/docker/web/nspanelmanager/web/views.py index 4052d29b..e6a77c9b 100755 --- a/docker/web/nspanelmanager/web/views.py +++ b/docker/web/nspanelmanager/web/views.py @@ -24,8 +24,11 @@ def restart_mqtt_manager(): def index(request): - print(len(Room.objects.all())) - return render(request, 'index.html', {'nspanels': NSPanel.objects.all()}) + if get_setting_with_default("use_farenheit", False) == "True": + temperature_unit = "°F" + else: + temperature_unit = "°C" + return render(request, 'index.html', {'nspanels': NSPanel.objects.all(), "temperature_unit": temperature_unit}) def rooms(request): @@ -104,11 +107,13 @@ def save_new_room(request): new_room = Room() new_room.friendly_name = request.POST['friendly_name'] new_room.save() + restart_mqtt_manager() return redirect('edit_room', room_id=new_room.id) def delete_room(request, room_id: int): Room.objects.filter(id=room_id).delete() + restart_mqtt_manager() return redirect('rooms') @@ -116,6 +121,7 @@ def update_room_form(request, room_id: int): room = Room.objects.filter(id=room_id).first() room.friendly_name = request.POST['friendly_name'] room.save() + restart_mqtt_manager() return redirect('edit_room', room_id=room_id) @@ -214,12 +220,14 @@ def add_scene_to_room(request, room_id: int): new_scene.friendly_name = request.POST["scene_name"] new_scene.room = room new_scene.save() + restart_mqtt_manager() return redirect('edit_room', room_id=room_id) def delete_scene(request, scene_id: int): scene = Scene.objects.get(id=scene_id) if scene: scene.delete() + restart_mqtt_manager() return redirect('edit_room', room_id=scene.room.id) def add_light_to_room_view(request, room_id: int): @@ -285,6 +293,7 @@ def settings_page(request): data["screensaver_dim_level"] = get_setting_with_default("screensaver_dim_level", 0) data["show_screensaver_clock"] = get_setting_with_default("show_screensaver_clock", False) data["clock_us_style"] = get_setting_with_default("clock_us_style", False) + data["use_farenheit"] = get_setting_with_default("use_farenheit", False) return render(request, 'settings.html', data) @@ -322,6 +331,7 @@ def save_settings(request): set_setting_value(name="screensaver_dim_level", value=request.POST["screensaver_dim_level"]) set_setting_value(name="show_screensaver_clock", value=("show_screensaver_clock" in request.POST)) set_setting_value(name="clock_us_style", value=("clock_us_style" in request.POST)) + set_setting_value(name="use_farenheit", value=("use_farenheit" in request.POST)) # Settings saved, restart mqtt_manager restart_mqtt_manager() return redirect('settings') diff --git a/firmware/NSPanelManagerFirmware/build_image.sh b/firmware/NSPanelManagerFirmware/build_image.sh index d972981b..61166f6a 100755 --- a/firmware/NSPanelManagerFirmware/build_image.sh +++ b/firmware/NSPanelManagerFirmware/build_image.sh @@ -5,10 +5,16 @@ boot_app0_bin_path="$pio_core_path/packages/framework-arduinoespressif32/tools/p if [ ! -e "$boot_app0_bin_path" ]; then echo "boot_app0.bin file does not exist as $boot_app0_bin_path!" - echo "Have you installed the esp32 platform in PlatformIO" + echo "Have you installed the esp32 platform in PlatformIO?" exit 1 fi +function get_partition_offset { + partition="$1" + partition_offset=$(grep -Eve "^#" partitions.csv | grep "$partition," | awk '{ print $4 }' | cut -d ',' -f 1) + echo $partition_offset +} + echo "Compile firmware" platformio run --environment esp32dev @@ -16,4 +22,4 @@ echo "Build LittleFS image" platformio run --target buildfs --environment esp32dev echo "Building image" -esptool.py --chip esp32 merge_bin -o merged-flash.bin --flash_mode dio --flash_size 4MB 0x1000 .pio/build/esp32dev/bootloader.bin 0x8000 .pio/build/esp32dev/partitions.bin 0xe000 "$boot_app0_bin_path" 0x10000 .pio/build/esp32dev/firmware.bin 2686976 .pio/build/esp32dev/littlefs.bin +esptool.py --chip esp32 merge_bin -o merged-flash.bin --flash_mode dio --flash_size 4MB 0x1000 .pio/build/esp32dev/bootloader.bin 0x8000 .pio/build/esp32dev/partitions.bin 0xe000 "$boot_app0_bin_path" 0x10000 .pio/build/esp32dev/firmware.bin $(get_partition_offset spiffs) .pio/build/esp32dev/littlefs.bin diff --git a/firmware/NSPanelManagerFirmware/data/index.html b/firmware/NSPanelManagerFirmware/data/index.html index c10b2105..ea6c6b7d 100644 --- a/firmware/NSPanelManagerFirmware/data/index.html +++ b/firmware/NSPanelManagerFirmware/data/index.html @@ -6,21 +6,9 @@ + - - +