diff --git a/Config.h b/Config.h index 2b7f76fe..c8f1bf40 100644 --- a/Config.h +++ b/Config.h @@ -2,6 +2,10 @@ #define NUKI_HUB_VERSION "8.32" +#define GITHUB_LATEST_RELEASE_URL "https://github.com/technyon/nuki_hub/releases/latest" +#define GITHUB_LATEST_RELEASE_API_URL "https://api.github.com/repos/technyon/nuki_hub/releases/latest" +#define GITHUB_LATEST_RELEASE_BINARY_URL "https://github.com/technyon/nuki_hub/raw/master/webflash/nuki_hub.bin" + #define MQTT_QOS_LEVEL 1 #define MQTT_CLEAN_SESSIONS false diff --git a/MqttTopics.h b/MqttTopics.h index 650a0545..78fc7a03 100644 --- a/MqttTopics.h +++ b/MqttTopics.h @@ -41,6 +41,7 @@ #define mqtt_topic_info_hardware_version "/info/hardwareVersion" #define mqtt_topic_info_firmware_version "/info/firmwareVersion" #define mqtt_topic_info_nuki_hub_version "/info/nukiHubVersion" +#define mqtt_topic_info_nuki_hub_latest "/info/nukiHubLatest" #define mqtt_topic_info_nuki_hub_ip "/info/nukiHubIp" #define mqtt_topic_keypad "/keypad" diff --git a/Network.cpp b/Network.cpp index d991a63e..ba688afe 100644 --- a/Network.cpp +++ b/Network.cpp @@ -357,7 +357,32 @@ bool Network::update() } _lastMaintenanceTs = ts; } + + if(_preferences->getBool(preference_check_updates)) + { + if(_lastUpdateCheckTs == 0 || (ts - _lastUpdateCheckTs) > 86400000) + { + _lastUpdateCheckTs = ts; + + https.useHTTP10(true); + https.begin(GITHUB_LATEST_RELEASE_API_URL); + + int httpResponseCode = https.GET(); + + if (httpResponseCode == HTTP_CODE_OK || httpResponseCode == HTTP_CODE_MOVED_PERMANENTLY) { + DynamicJsonDocument doc(6144); + DeserializationError jsonError = deserializeJson(doc, https.getStream()); + + if (!jsonError) { + _latestVersion = doc["tag_name"]; + publishString(_maintenancePathPrefix, mqtt_topic_info_nuki_hub_latest, _latestVersion); + } + } + https.end(); + } + } + for(const auto& gpioTs : _gpioTs) { uint8_t pin = gpioTs.first; @@ -644,6 +669,11 @@ int Network::mqttConnectionState() return _mqttConnectionState; } +const char* Network::latestHubVersion() +{ + return _latestVersion; +} + bool Network::encryptionSupported() { return _device->supportsEncryption(); @@ -915,6 +945,54 @@ void Network::publishHASSConfig(char* deviceType, const char* baseTopic, char* n "", { { "enabled_by_default", "true" }, {"ic", "mdi:counter"}}); + + if(_preferences->getBool(preference_check_updates)) + { + // NUKI Hub latest + publishHassTopic("sensor", + "nuki_hub_latest", + uidString, + "_nuki_hub_latest", + "NUKI Hub latest", + name, + baseTopic, + _lockPath + mqtt_topic_info_nuki_hub_latest, + deviceType, + "", + "", + "diagnostic", + "", + { { "enabled_by_default", "true" }, + {"ic", "mdi:counter"}}); + + // NUKI Hub update + char latest_version_topic[250]; + _lockPath.toCharArray(latest_version_topic,_lockPath.length() + 1); + strcat(latest_version_topic, mqtt_topic_info_nuki_hub_latest); + + publishHassTopic("update", + "nuki_hub_update", + uidString, + "_nuki_hub_update", + "NUKI Hub firmware update", + name, + baseTopic, + _lockPath + mqtt_topic_info_nuki_hub_version, + deviceType, + "firmware", + "", + "diagnostic", + "", + { { "enabled_by_default", "true" }, + { "entity_picture", "https://raw.githubusercontent.com/technyon/nuki_hub/master/icon/favicon-32x32.png" }, + { "release_url", GITHUB_LATEST_RELEASE_URL }, + { "latest_version_topic", latest_version_topic }}); + } + else + { + removeHassTopic("sensor", "nuki_hub_latest", uidString); + removeHassTopic("update", "nuki_hub_update", uidString); + } // Nuki Hub IP Address publishHassTopic("sensor", @@ -1031,8 +1109,6 @@ void Network::publishHASSConfigAdditionalButtons(char *deviceType, const char *b { "pl_prs", "lockNgoUnlatch" }}); } - -//json["cmd_t"] = String("~") + String(mqtt_topic_lock_action); void Network::publishHASSConfigBatLevel(char *deviceType, const char *baseTopic, char *name, char *uidString) { String discoveryTopic = _preferences->getString(preference_mqtt_hass_discovery); @@ -1356,95 +1432,34 @@ void Network::removeHASSConfig(char* uidString) if(discoveryTopic != "") { - String path = discoveryTopic; - path.concat("/lock/"); - path.concat(uidString); - path.concat("/smartlock/config"); - _device->mqttPublish(path.c_str(), MQTT_QOS_LEVEL, true, ""); - - path = discoveryTopic; - path.concat("/binary_sensor/"); - path.concat(uidString); - path.concat("/battery_low/config"); - _device->mqttPublish(path.c_str(), MQTT_QOS_LEVEL, true, ""); - - path = discoveryTopic; - path.concat("/button/"); - path.concat(uidString); - path.concat("/lockngo/config"); - _device->mqttPublish(path.c_str(), MQTT_QOS_LEVEL, true, ""); - - path = discoveryTopic; - path.concat("/button/"); - path.concat(uidString); - path.concat("/lockngounlatch/config"); - _device->mqttPublish(path.c_str(), MQTT_QOS_LEVEL, true, ""); - - path = discoveryTopic; - path.concat("/button/"); - path.concat(uidString); - path.concat("/unlatch/config"); - _device->mqttPublish(path.c_str(), MQTT_QOS_LEVEL, true, ""); - - path = discoveryTopic; - path.concat("/sensor/"); - path.concat(uidString); - path.concat("/battery_voltage/config"); - _device->mqttPublish(path.c_str(), MQTT_QOS_LEVEL, true, ""); - - path = discoveryTopic; - path.concat("/sensor/"); - path.concat(uidString); - path.concat("/trigger/config"); - _device->mqttPublish(path.c_str(), MQTT_QOS_LEVEL, true, ""); - - path = discoveryTopic; - path.concat("/sensor/"); - path.concat(uidString); - path.concat("/battery_level/config"); - _device->mqttPublish(path.c_str(), MQTT_QOS_LEVEL, true, ""); - - path = discoveryTopic; - path.concat("/sensor/"); - path.concat(uidString); - path.concat("/sound_level/config"); - _device->mqttPublish(path.c_str(), MQTT_QOS_LEVEL, true, ""); - - path = discoveryTopic; - path.concat("/sensor/"); - path.concat(uidString); - path.concat("/nuki_hub_ip/config"); - _device->mqttPublish(path.c_str(), MQTT_QOS_LEVEL, true, ""); - - path = discoveryTopic; - path.concat("/number/"); - path.concat(uidString); - path.concat("/sound_level/config"); - _device->mqttPublish(path.c_str(), MQTT_QOS_LEVEL, true, ""); - - path = discoveryTopic; - path.concat("/binary_sensor/"); - path.concat(uidString); - path.concat("/door_sensor/config"); - _device->mqttPublish(path.c_str(), MQTT_QOS_LEVEL, true, ""); - - path = discoveryTopic; - path.concat("/binary_sensor/"); - path.concat(uidString); - path.concat("/ring/config"); - _device->mqttPublish(path.c_str(), MQTT_QOS_LEVEL, true, ""); - - path = discoveryTopic; - path.concat("/sensor/"); - path.concat(uidString); - path.concat("/wifi_signal_strength/config"); - _device->mqttPublish(path.c_str(), MQTT_QOS_LEVEL, true, ""); - - path = discoveryTopic; - path.concat("/sensor/"); - path.concat(uidString); - path.concat("/bluetooth_signal_strength/config"); - _device->mqttPublish(path.c_str(), MQTT_QOS_LEVEL, true, ""); + removeHassTopic("lock", "smartlock", uidString); + removeHassTopic("binary_sensor", "battery_low", uidString); + removeHassTopic("binary_sensor", "keypad_battery_low", uidString); + removeHassTopic("sensor", "battery_voltage", uidString); + removeHassTopic("sensor", "trigger", uidString); + removeHassTopic("binary_sensor", "mqtt_connected", uidString); + removeHassTopic("switch", "reset", uidString); + removeHassTopic("sensor", "firmware_version", uidString); + removeHassTopic("sensor", "hardware_version", uidString); + removeHassTopic("sensor", "nuki_hub_version", uidString); + removeHassTopic("sensor", "nuki_hub_latest", uidString); + removeHassTopic("update", "nuki_hub_update", uidString); + removeHassTopic("sensor", "nuki_hub_ip", uidString); + removeHassTopic("switch", "led_enabled", uidString); + removeHassTopic("switch", "button_enabled", uidString); + removeHassTopic("button", "unlatch", uidString); + removeHassTopic("button", "lockngo", uidString); + removeHassTopic("button", "lockngounlatch", uidString); + removeHassTopic("sensor", "battery_level", uidString); + removeHassTopic("binary_sensor", "door_sensor", uidString); + removeHassTopic("binary_sensor", "ring", uidString); + removeHassTopic("number", "led_brightness", uidString); + removeHassTopic("sensor", "sound_level", uidString); + removeHassTopic("number", "sound_level", uidString); + removeHassTopic("sensor", "last_action_authorization", uidString); + removeHassTopic("sensor", "keypad_status", uidString); + removeHassTopic("sensor", "wifi_signal_strength", uidString); + removeHassTopic("sensor", "bluetooth_signal_strength", uidString); } } diff --git a/Network.h b/Network.h index c9aa133b..009511cd 100644 --- a/Network.h +++ b/Network.h @@ -8,6 +8,7 @@ #include "networkDevices/IPConfiguration.h" #include "MqttTopics.h" #include "Gpio.h" +#include enum class NetworkDeviceType { @@ -62,6 +63,7 @@ class Network void publishPresenceDetection(char* csv); int mqttConnectionState(); // 0 = not connected; 1 = connected; 2 = connected and mqtt processed + const char* latestHubVersion(); bool encryptionSupported(); const String networkDeviceName() const; @@ -111,6 +113,9 @@ class Network char _mqttConnectionStateTopic[211] = {0}; String _lockPath; + const char* _latestVersion; + HTTPClient https; + Preferences* _preferences; Gpio* _gpio; IPConfiguration* _ipConfiguration = nullptr; @@ -137,6 +142,7 @@ class Network unsigned long _lastConnectedTs = 0; unsigned long _lastMaintenanceTs = 0; + unsigned long _lastUpdateCheckTs = 0; unsigned long _lastRssiTs = 0; bool _mqttEnabled = true; static unsigned long _ignoreSubscriptionsTs; diff --git a/PreferencesKeys.h b/PreferencesKeys.h index 13757d53..c1632f44 100644 --- a/PreferencesKeys.h +++ b/PreferencesKeys.h @@ -14,6 +14,7 @@ #define preference_mqtt_lock_path "mqttpath" #define preference_opener_enabled "openerena" #define preference_mqtt_opener_path "mqttoppath" +#define preference_check_updates "checkupdates" #define preference_lock_max_keypad_code_count "maxkpad" #define preference_opener_max_keypad_code_count "opmaxkpad" #define preference_mqtt_ca "mqttca" @@ -62,7 +63,7 @@ class DebugPreferences std::vector _keys = { preference_started_before, preference_device_id_lock, preference_device_id_opener, preference_mqtt_broker, preference_mqtt_broker_port, - preference_mqtt_user, preference_mqtt_password, preference_mqtt_log_enabled, preference_lock_enabled, + preference_mqtt_user, preference_mqtt_password, preference_mqtt_log_enabled, preference_check_updates, preference_lock_enabled, preference_mqtt_lock_path, preference_opener_enabled, preference_mqtt_opener_path, preference_lock_max_keypad_code_count, preference_opener_max_keypad_code_count, preference_mqtt_ca, preference_mqtt_crt, preference_mqtt_key, preference_mqtt_hass_discovery, preference_mqtt_hass_cu_url, @@ -85,7 +86,7 @@ class DebugPreferences }; std::vector _boolPrefs = { - preference_started_before, preference_mqtt_log_enabled, preference_lock_enabled, preference_opener_enabled, + preference_started_before, preference_mqtt_log_enabled, preference_check_updates, preference_lock_enabled, preference_opener_enabled, preference_restart_on_disconnect, preference_keypad_control_enabled, preference_register_as_app, preference_ip_dhcp_enabled, preference_publish_authdata, preference_has_mac_saved, preference_publish_debug_info, preference_network_wifi_fallback_disabled }; diff --git a/WebCfgServer.cpp b/WebCfgServer.cpp index e138f8ba..daacb29a 100644 --- a/WebCfgServer.cpp +++ b/WebCfgServer.cpp @@ -356,7 +356,7 @@ bool WebCfgServer::processArgs(String& message) { _preferences->putString(preference_mqtt_hass_cu_url, value); configChanged = true; - } + } else if(key == "HOSTNAME") { _preferences->putString(preference_hostname, value); @@ -377,6 +377,11 @@ bool WebCfgServer::processArgs(String& message) _preferences->putBool(preference_mqtt_log_enabled, (value == "1")); configChanged = true; } + else if(key == "CHECKUPDATE") + { + _preferences->putBool(preference_check_updates, (value == "1")); + configChanged = true; + } else if(key == "DHCPENA") { _preferences->putBool(preference_ip_dhcp_enabled, (value == "1")); @@ -617,6 +622,16 @@ void WebCfgServer::buildHtml(String& response) printParameter(response, "Nuki Opener state", lockstateArr); } printParameter(response, "Firmware", version.c_str(), "/info"); + + const char* _latestVersion = _network->latestHubVersion(); + + if(_preferences->getBool(preference_check_updates)) + { + //if(atof(_latestVersion) > atof(NUKI_HUB_VERSION) || (atof(_latestVersion) == atof(NUKI_HUB_VERSION) && _latestVersion != NUKI_HUB_VERSION)) { + printParameter(response, "Latest Firmware", _latestVersion, "/ota"); + //} + } + response.concat("

"); response.concat("

MQTT and Network Configuration

"); @@ -726,6 +741,18 @@ void WebCfgServer::buildOtaHtml(String &response, bool errored) response.concat("
Choose the updated nuki_hub.bin file to upload:
"); response.concat("
"); + + if(_preferences->getBool(preference_check_updates)) + { + response.concat(""); + + response.concat("

"); + } + response.concat("
Initiating Over-the-air update. This will take about two minutes, please be patient.
You will be forwarded automatically when the update is complete.
"); response.concat("