From c2cde43a54acbc00e233e3d36a55d29391668f67 Mon Sep 17 00:00:00 2001 From: Theo Arends <11044339+arendst@users.noreply.github.com> Date: Sun, 11 Apr 2021 13:29:33 +0200 Subject: [PATCH] Refactor Tasmota Discovery --- tasmota/support_network.ino | 6 + tasmota/support_tasmota.ino | 7 +- tasmota/xdrv_10_rules.ino | 4 +- tasmota/xdrv_12_discovery.ino | 209 ++++++++++++++++------------- tasmota/xdrv_12_home_assistant.ino | 4 +- tasmota/xdrv_20_hue.ino | 6 +- 6 files changed, 131 insertions(+), 105 deletions(-) diff --git a/tasmota/support_network.ino b/tasmota/support_network.ino index 242c03121..c033b0fdc 100644 --- a/tasmota/support_network.ino +++ b/tasmota/support_network.ino @@ -127,3 +127,9 @@ String NetworkMacAddress(void) { #endif return WiFi.macAddress(); } + +String NetworkUniqueId(void) { + String unique_id = WiFi.macAddress(); + unique_id.replace(":", ""); // Full 12 chars MAC address as ID + return unique_id; +} diff --git a/tasmota/support_tasmota.ino b/tasmota/support_tasmota.ino index 078d32b8c..05e6e3974 100644 --- a/tasmota/support_tasmota.ino +++ b/tasmota/support_tasmota.ino @@ -43,8 +43,7 @@ char* Format(char* output, const char* input_p, int size) snprintf_P(tmp, size, PSTR("%s%c0%dd"), output, '%', digits); snprintf_P(output, size, tmp, ESP_getChipId() & 0x1fff); // %04d - short chip ID in dec, like in hostname } else { - String mac_address = WiFi.macAddress(); - mac_address.replace(":", ""); + String mac_address = NetworkUniqueId(); if (digits > 12) { digits = 12; } String mac_part = mac_address.substring(12 - digits); snprintf_P(output, size, PSTR("%s%s"), output, mac_part.c_str()); // %01X .. %12X - mac address in hex @@ -122,9 +121,7 @@ char* GetTopic_P(char *stopic, uint32_t prefix, char *topic, const char* subtopi fulltopic.replace(FPSTR(MQTT_TOKEN_TOPIC), (const __FlashStringHelper *)topic); fulltopic.replace(F("%hostname%"), TasmotaGlobal.hostname); - String token_id = WiFi.macAddress(); - token_id.replace(":", ""); - fulltopic.replace(F("%id%"), token_id); + fulltopic.replace(F("%id%"), NetworkUniqueId()); } fulltopic.replace(F("#"), ""); fulltopic.replace(F("//"), "/"); diff --git a/tasmota/xdrv_10_rules.ino b/tasmota/xdrv_10_rules.ino index d356fd598..cf539ec6b 100644 --- a/tasmota/xdrv_10_rules.ino +++ b/tasmota/xdrv_10_rules.ino @@ -759,9 +759,7 @@ bool RuleSetProcess(uint8_t rule_set, String &event_saved) RulesVarReplace(commands, F("%TOPIC%"), TasmotaGlobal.mqtt_topic); snprintf_P(stemp, sizeof(stemp), PSTR("%06X"), ESP_getChipId()); RulesVarReplace(commands, F("%DEVICEID%"), stemp); - String mac_address = WiFi.macAddress(); - mac_address.replace(":", ""); - RulesVarReplace(commands, F("%MACADDR%"), mac_address); + RulesVarReplace(commands, F("%MACADDR%"), NetworkUniqueId()); #if defined(USE_TIMERS) && defined(USE_SUNRISE) RulesVarReplace(commands, F("%SUNRISE%"), String(SunMinutes(0))); RulesVarReplace(commands, F("%SUNSET%"), String(SunMinutes(1))); diff --git a/tasmota/xdrv_12_discovery.ino b/tasmota/xdrv_12_discovery.ino index 0b16945f0..bdc31e4cc 100644 --- a/tasmota/xdrv_12_discovery.ino +++ b/tasmota/xdrv_12_discovery.ino @@ -32,52 +32,69 @@ #define XDRV_12 12 -const char TASMOTA_DISCOVER_DEVICE[] PROGMEM = // Basic parameters for Discovery - "{\"ip\":\"%_I\"," // IP Address - "\"dn\":\"%s\"," // Device Name - "\"fn\":[%s]," // Friendly Names - "\"hn\":\"%s\"," // Host Name - "\"mac\":\"%s\"," // Full MAC as Device id - "\"md\":\"%s\"," // Module or Template Name - "\"ty\":%d,\"if\":%d," // Flag for TuyaMCU and Ifan devices - "\"ofln\":\"" MQTT_LWT_OFFLINE "\"," // Payload Offline - "\"onln\":\"" MQTT_LWT_ONLINE "\"," // Payload Online - "\"state\":[\"%s\",\"%s\",\"%s\",\"%s\"]," // State text for "OFF","ON","TOGGLE","HOLD" - "\"sw\":\"%s\"," // Software Version - "\"t\":\"%s\"," // Topic - "\"ft\":\"%s\"," // Full Topic - "\"tp\":[\"%s\",\"%s\",\"%s\"]," // Topics for command, stat and tele - "\"rl\":[%s],\"swc\":[%s],\"swn\":[%s],\"btn\":[%s]," // Inputs / Outputs - "\"so\":{\"4\":%d,\"11\":%d,\"13\":%d,\"17\":%d,\"20\":%d," // SetOptions - "\"30\":%d,\"68\":%d,\"73\":%d,\"82\":%d,\"114\":%d,\"117\":%d}," - "\"lk\":%d,\"lt_st\":%d,\"sho\":[%s],\"ver\":1}"; // Light SubType, Shutter Options and Discovery version +uint8_t TasDiscoverData_init_step; -struct { - uint8_t init_step; -} TasDiscoverData; +void TasDiscoverMessage(void) { + Response_P(PSTR("{\"ip\":\"%_I\"," // IP Address + "\"dn\":\"%s\"," // Device Name + "\"fn\":["), // Friendly Names (start) + (uint32_t)WiFi.localIP(), + SettingsText(SET_DEVICENAME)); -void TasDiscovery(void) { - bool iFan = false; + uint32_t maxfn = (TasmotaGlobal.devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!TasmotaGlobal.devices_present) ? 1 : TasmotaGlobal.devices_present; + for (uint32_t i = 0; i < MAX_FRIENDLYNAMES; i++) { + char fname[TOPSZ]; + snprintf_P(fname, sizeof(fname), PSTR("\"%s\""), EscapeJSONString(SettingsText(SET_FRIENDLYNAME1 +i)).c_str()); + ResponseAppend_P(PSTR("%s%s"), (i > 0 ? "," : ""), (i < maxfn) ? fname : PSTR("null")); + } + + bool TuyaMod = false; + bool iFanMod = false; #ifdef ESP8266 - if ((SONOFF_IFAN02 == TasmotaGlobal.module_type) || (SONOFF_IFAN03 == TasmotaGlobal.module_type)) { iFan = true; } + if ((TUYA_DIMMER == TasmotaGlobal.module_type) || (SK03_TUYA == TasmotaGlobal.module_type)) { TuyaMod = true; }; + if ((SONOFF_IFAN02 == TasmotaGlobal.module_type) || (SONOFF_IFAN03 == TasmotaGlobal.module_type)) { iFanMod = true; }; #endif // ESP8266 - uint8_t lightidx = MAX_RELAYS + 1; // Will store the starting position of the lights + ResponseAppend_P(PSTR("]," // Friendly Names (end) + "\"hn\":\"%s\"," // Host Name + "\"mac\":\"%s\"," // Full MAC as Device id + "\"md\":\"%s\"," // Module or Template Name + "\"ty\":%d,\"if\":%d," // Flag for TuyaMCU and Ifan devices + "\"ofln\":\"" MQTT_LWT_OFFLINE "\"," // Payload Offline + "\"onln\":\"" MQTT_LWT_ONLINE "\"," // Payload Online + "\"state\":[\"%s\",\"%s\",\"%s\",\"%s\"]," // State text for "OFF","ON","TOGGLE","HOLD" + "\"sw\":\"%s\"," // Software Version + "\"t\":\"%s\"," // Topic + "\"ft\":\"%s\"," // Full Topic + "\"tp\":[\"%s\",\"%s\",\"%s\"]," // Topics for command, stat and tele + "\"rl\":["), // Relays (start) + TasmotaGlobal.hostname, + NetworkUniqueId().c_str(), + ModuleName().c_str(), + TuyaMod, iFanMod, + GetStateText(0), GetStateText(1), GetStateText(2), GetStateText(3), + TasmotaGlobal.version, + TasmotaGlobal.mqtt_topic, + SettingsText(SET_MQTT_FULLTOPIC), + PSTR(SUB_PREFIX), + PSTR(PUB_PREFIX), + PSTR(PUB_PREFIX2)); + + uint8_t lightidx = MAX_RELAYS + 1; // Will store the starting position of the lights if (Light.subtype > LST_NONE) { - if (!light_controller.isCTRGBLinked()) { // One or two lights present + if (!light_controller.isCTRGBLinked()) { // One or two lights present lightidx = TasmotaGlobal.devices_present - 2; } else { lightidx = TasmotaGlobal.devices_present - 1; } } - if ((Light.device > 0) && Settings.flag3.pwm_multi_channels) { // How many relays are light devices? + if ((Light.device > 0) && Settings.flag3.pwm_multi_channels) { // How many relays are light devices? lightidx = TasmotaGlobal.devices_present - Light.subtype; } - uint16_t Relay[MAX_RELAYS] = { 0 }; // Base array to store the relay type - uint16_t Shutter[MAX_RELAYS] = { 0 }; // Array to store a temp list for shutters - char RelLst[MAX_RELAYS*2] = { 0 }; // Relay as a char list, "0,0,0,0,0,0,0,0" + uint16_t Relay[MAX_RELAYS] = { 0 }; // Base array to store the relay type + uint16_t Shutter[MAX_RELAYS] = { 0 }; // Array to store a temp list for shutters for (uint32_t i = 0; i < MAX_RELAYS; i++) { if (i < TasmotaGlobal.devices_present) { @@ -95,104 +112,116 @@ void TasDiscovery(void) { } #endif // USE_SHUTTER - if (Shutter[i] != 0) { // Check if there are shutters present - Relay[i] = 3; // Relay is a shutter + if (Shutter[i] != 0) { // Check if there are shutters present + Relay[i] = 3; // Relay is a shutter } else { - if (i >= lightidx || (iFan && i == 0)) { // First relay on Ifan controls the light - Relay[i] = 2; // Relay is a light + if (i >= lightidx || (iFanMod && (0 == i))) { // First relay on Ifan controls the light + Relay[i] = 2; // Relay is a light } else { - if (!iFan) { // Relays 2-4 for ifan are controlled by FANSPEED and don't need to be present if TasmotaGlobal.module_type = SONOFF_IFAN02 or SONOFF_IFAN03 - Relay[i] = 1; // Simple Relay + if (!iFanMod) { // Relays 2-4 for ifan are controlled by FANSPEED and don't need to be present if TasmotaGlobal.module_type = SONOFF_IFAN02 or SONOFF_IFAN03 + Relay[i] = 1; // Simple Relay } } } } - snprintf_P(RelLst, sizeof(RelLst), PSTR("%s%s%d"), RelLst, (i > 0 ? "," : ""), Relay[i]); // Vector for the Official Integration + ResponseAppend_P(PSTR("%s%d"), (i > 0 ? "," : ""), Relay[i]); // Vector for the Official Integration } - bool TuyaMod = false; - bool iFanMod = false; -#ifdef ESP8266 - if ((TUYA_DIMMER == TasmotaGlobal.module_type) || (SK03_TUYA == TasmotaGlobal.module_type)) { TuyaMod = true; }; - if ((SONOFF_IFAN02 == TasmotaGlobal.module_type) || (SONOFF_IFAN03 == TasmotaGlobal.module_type)) { iFanMod = true; }; -#endif // ESP8266 + ResponseAppend_P(PSTR("]," // Relays (end) + "\"swc\":[")); // Switch modes (start) - char friendly_name[200]; - friendly_name[0] = '\0'; - uint32_t maxfn = (TasmotaGlobal.devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!TasmotaGlobal.devices_present) ? 1 : TasmotaGlobal.devices_present; - for (uint32_t i = 0; i < MAX_FRIENDLYNAMES; i++) { - char fname[TOPSZ]; - snprintf_P(fname, sizeof(fname), PSTR("\"%s\""), EscapeJSONString(SettingsText(SET_FRIENDLYNAME1 +i)).c_str()); - snprintf_P(friendly_name, sizeof(friendly_name), PSTR("%s%s%s"), friendly_name, (i > 0 ? "," : ""), (i < maxfn) ? fname : PSTR("null")); + // Enable Discovery for Switches only if SetOption114 is enabled + for (uint32_t i = 0; i < MAX_SWITCHES; i++) { + ResponseAppend_P(PSTR("%s%d"), (i > 0 ? "," : ""), (PinUsed(GPIO_SWT1, i) && Settings.flag5.mqtt_switches) ? Settings.switchmode[i] : -1); } - char switch_mode[90]; - switch_mode[0] = '\0'; - char switch_name[300]; - switch_name[0] = '\0'; + ResponseAppend_P(PSTR("]," // Switch modes (end) + "\"swn\":[")); // Switch names (start) + // Enable Discovery for Switches only if SetOption114 is enabled for (uint32_t i = 0; i < MAX_SWITCHES; i++) { char sname[TOPSZ]; snprintf_P(sname, sizeof(sname), PSTR("\"%s\""), GetSwitchText(i).c_str()); - snprintf_P(switch_mode, sizeof(switch_mode), PSTR("%s%s%d"), switch_mode, (i > 0 ? "," : ""), (PinUsed(GPIO_SWT1, i) & Settings.flag5.mqtt_switches) ? Settings.switchmode[i] : -1); - snprintf_P(switch_name, sizeof(switch_name), PSTR("%s%s%s"), switch_name, (i > 0 ? "," : ""), (PinUsed(GPIO_SWT1, i) & Settings.flag5.mqtt_switches) ? sname : PSTR("null")); + ResponseAppend_P(PSTR("%s%s"), (i > 0 ? "," : ""), (PinUsed(GPIO_SWT1, i) && Settings.flag5.mqtt_switches) ? sname : PSTR("null")); } + ResponseAppend_P(PSTR("]," // Switch names (end) + "\"btn\":[")); // Button flag (start) + bool SerialButton = false; - char button_flag[90]; - button_flag[0] = '\0'; // Enable Discovery for Buttons only if SetOption73 is enabled for (uint32_t i = 0; i < MAX_KEYS; i++) { #ifdef ESP8266 SerialButton = ((0 == i) && (SONOFF_DUAL == TasmotaGlobal.module_type )); -#endif // ESP8266 - snprintf_P(button_flag, sizeof(button_flag), PSTR("%s%s%d"), button_flag, (i > 0 ? "," : ""), (SerialButton ? 1 : (PinUsed(GPIO_KEY1, i)) & Settings.flag3.mqtt_buttons)); +#endif // ESP8266 + ResponseAppend_P(PSTR("%s%d"), (i > 0 ? "," : ""), (SerialButton ? 1 : (PinUsed(GPIO_KEY1, i)) && Settings.flag3.mqtt_buttons)); } - char shutter_option[90]; - shutter_option[0] = '\0'; + ResponseAppend_P(PSTR("]," // Button flag (end) + "\"so\":{\"4\":%d," // SetOptions + "\"11\":%d," + "\"13\":%d," + "\"17\":%d," + "\"20\":%d," + "\"30\":%d," + "\"68\":%d," + "\"73\":%d," + "\"82\":%d," + "\"114\":%d," + "\"117\":%d}," + "\"lk\":%d," // Light CTRGB linked + "\"lt_st\":%d," // Light SubType + "\"sho\":["), // Shutter Options (start) + Settings.flag.mqtt_response, + Settings.flag.button_swap, + Settings.flag.button_single, + Settings.flag.decimal_text, + Settings.flag.not_power_linked, + Settings.flag.hass_light, + Settings.flag3.pwm_multi_channels, + Settings.flag3.mqtt_buttons, + Settings.flag4.alexa_ct_range, + Settings.flag5.mqtt_switches, + Settings.flag5.fade_fixed_duration, + light_controller.isCTRGBLinked(), + Light.subtype); + for (uint32_t i = 0; i < MAX_SHUTTERS; i++) { #ifdef USE_SHUTTER - snprintf_P(shutter_option, sizeof(shutter_option), PSTR("%s%s%d"), shutter_option, (i > 0 ? "," : ""), Settings.shutter_options[i]); + ResponseAppend_P(PSTR("%s%d"), (i > 0 ? "," : ""), Settings.shutter_options[i]); #else - snprintf_P(shutter_option, sizeof(shutter_option), PSTR("%s%s0"), shutter_option, (i > 0 ? "," : "")); + ResponseAppend_P(PSTR("%s0"), (i > 0 ? "," : "")); #endif // USE_SHUTTER } - // Full 12 chars MAC address as ID - String mac_address = WiFi.macAddress(); - mac_address.replace(":", ""); - char unique_id[30]; - snprintf_P(unique_id, sizeof(unique_id), PSTR("%s"), mac_address.c_str()); + ResponseAppend_P(PSTR("]," // Shutter Options (end) + "\"ver\":1}")); // Discovery version +} - TasmotaGlobal.masterlog_level = LOG_LEVEL_DEBUG_MORE; // Hide topic on clean and remove use weblog 4 to show it +void TasDiscovery(void) { + TasmotaGlobal.masterlog_level = LOG_LEVEL_DEBUG_MORE; // Hide topic on clean and remove use weblog 4 to show it - ResponseClear(); // Clear retained message - if (!Settings.flag.hass_discovery) { // SetOption19 - Clear retained message - Response_P(TASMOTA_DISCOVER_DEVICE, (uint32_t)WiFi.localIP(), SettingsText(SET_DEVICENAME), - friendly_name, TasmotaGlobal.hostname, unique_id, ModuleName().c_str(), TuyaMod, iFanMod, GetStateText(0), GetStateText(1), GetStateText(2), GetStateText(3), - TasmotaGlobal.version, TasmotaGlobal.mqtt_topic, SettingsText(SET_MQTT_FULLTOPIC), PSTR(SUB_PREFIX), PSTR(PUB_PREFIX), PSTR(PUB_PREFIX2), RelLst, switch_mode, switch_name, - button_flag, Settings.flag.mqtt_response, Settings.flag.button_swap, Settings.flag.button_single, Settings.flag.decimal_text, Settings.flag.not_power_linked, - Settings.flag.hass_light, Settings.flag3.pwm_multi_channels, Settings.flag3.mqtt_buttons, Settings.flag4.alexa_ct_range, Settings.flag5.mqtt_switches, - Settings.flag5.fade_fixed_duration, light_controller.isCTRGBLinked(), Light.subtype, shutter_option); + ResponseClear(); // Clear retained message + if (!Settings.flag.hass_discovery) { // SetOption19 - Clear retained message + TasDiscoverMessage(); // Build discovery message } char stopic[TOPSZ]; - snprintf_P(stopic, sizeof(stopic), PSTR("tasmota/discovery/%s/config"), unique_id); + snprintf_P(stopic, sizeof(stopic), PSTR("tasmota/discovery/%s/config"), NetworkUniqueId().c_str()); MqttPublish(stopic, true); - if (!Settings.flag.hass_discovery) { // SetOption19 - Clear retained message + if (!Settings.flag.hass_discovery) { // SetOption19 - Clear retained message Response_P(PSTR("{\"sn\":")); MqttShowSensor(); ResponseAppend_P(PSTR(",\"ver\":1}")); } - snprintf_P(stopic, sizeof(stopic), PSTR("tasmota/discovery/%s/sensors"), unique_id); + snprintf_P(stopic, sizeof(stopic), PSTR("tasmota/discovery/%s/sensors"), NetworkUniqueId().c_str()); MqttPublish(stopic, true); - TasmotaGlobal.masterlog_level = LOG_LEVEL_NONE; // Restore WebLog state + TasmotaGlobal.masterlog_level = LOG_LEVEL_NONE; // Restore WebLog state } void TasRediscover(void) { - TasDiscoverData.init_step = 1; // Delayed discovery or clear retained messages + TasDiscoverData_init_step = 1; // Delayed discovery or clear retained messages } /*********************************************************************************************\ @@ -202,18 +231,18 @@ void TasRediscover(void) { bool Xdrv12(uint8_t function) { bool result = false; - if (Settings.flag.mqtt_enabled) { // SetOption3 - Enable MQTT + if (Settings.flag.mqtt_enabled) { // SetOption3 - Enable MQTT switch (function) { case FUNC_EVERY_SECOND: - if (TasDiscoverData.init_step) { - TasDiscoverData.init_step--; - if (!TasDiscoverData.init_step) { - TasDiscovery(); // Send the topics for discovery + if (TasDiscoverData_init_step) { + TasDiscoverData_init_step--; + if (!TasDiscoverData_init_step) { + TasDiscovery(); // Send the topics for discovery } } break; case FUNC_MQTT_INIT: - TasDiscoverData.init_step = 10; // Delayed discovery + TasDiscoverData_init_step = 10; // Delayed discovery break; } } diff --git a/tasmota/xdrv_12_home_assistant.ino b/tasmota/xdrv_12_home_assistant.ino index 8f0f75941..b4d1a945a 100644 --- a/tasmota/xdrv_12_home_assistant.ino +++ b/tasmota/xdrv_12_home_assistant.ino @@ -329,9 +329,7 @@ void NewHAssDiscovery(void) ResponseClear(); // Clear retained message // Full 12 chars MAC address as ID - String mac_address = WiFi.macAddress(); - mac_address.replace(":", ""); - snprintf_P(unique_id, sizeof(unique_id), PSTR("%s"), mac_address.c_str()); + snprintf_P(unique_id, sizeof(unique_id), PSTR("%s"), NetworkUniqueId().c_str()); snprintf_P(stopic, sizeof(stopic), PSTR("tasmota/discovery/%s/config"), unique_id); // Send empty message if new discovery is disabled diff --git a/tasmota/xdrv_20_hue.ino b/tasmota/xdrv_20_hue.ino index db20ef24c..e41233b9c 100644 --- a/tasmota/xdrv_20_hue.ino +++ b/tasmota/xdrv_20_hue.ino @@ -172,8 +172,7 @@ const char HUE_API[] PROGMEM = "\x00\x06\x3B\x37\x8C\xEC\x2D\x10\xEC\x9C\x2F\x9D String HueBridgeId(void) { - String temp = WiFi.macAddress(); - temp.replace(":", ""); + String temp = NetworkUniqueId(); String bridgeid = temp.substring(0, 6); bridgeid += F("FFFE"); bridgeid += temp.substring(6); @@ -182,8 +181,7 @@ String HueBridgeId(void) String HueSerialnumber(void) { - String serial = WiFi.macAddress(); - serial.replace(":", ""); + String serial = NetworkUniqueId(); serial.toLowerCase(); return serial; // 5ccf7f139f3d }