diff --git a/docs/source/Plugin/DataAcquisition.repl b/docs/source/Plugin/DataAcquisition.repl index b75783dc4..59501345a 100644 --- a/docs/source/Plugin/DataAcquisition.repl +++ b/docs/source/Plugin/DataAcquisition.repl @@ -28,15 +28,17 @@ For each controller the user can select wether the data should be sent on each I For the Domoticz controllers the value index (IDX) has to be configured. -For some controllers, like Home Assistant/openHAB, there are extra options available. +For some controllers, like Home Assistant (openHAB) MQTT, there are extra options available. * **Group**: This represents the group id to combine all values from multiple tasks into a single grouped-device during MQTT AutoDiscovery. Groups, by design, can span multiple ESPEasy devices, if desired, as long as the Task/Valuename combinations are unique. If a group should only combine Tasks from a single ESPEasy unit, the group id should be unique across multiple ESPEasy units. The group description, default *Group *, can be adjusted in Home Assistant. If the Group value matches the current Unit nr, the Unit name, ``%sysname%``, is used instead of Group . + * When **Group** is used, the Discovery unique id, by default, *doesn't* include the taskname, causing devices with the same Value name in the same group to be overwritten in the MQTT broker. By setting the checkbox **MQTT Discover, Group incl. Taskname** on the Tools/Advanced page, the unique id will be made 'more unique' by including the Taskname. (Not enabled by default for backward compatibility). + * **Retained**: For MQTT Controllers, this setting can be enabled to send the values for the current task with the Retain flag set. The **Publish Retain flag** in the Controller settings will override this by sending *all* task values with Retain flag enabled. * **Send derived**: This checkbox determines if any configured Derived values should also be sent to the controller (and included in the AutoDiscovery if that's available and enabled). -* **Resend MQTT Discovery**: When checked, will start a resend of the MQTT Discovery process for this task after a random delay, when Submit is clicked, so any changed settings will be updated in the MQTT broker. This setting is only available if the controller is enabled, the Auto Discovery feature is available and enabled for the controller. This setting is not stored. +* **Resend MQTT Discovery**: When checked, will initiate a resend of the MQTT Discovery process for this task after a random delay after Submit is clicked, so any changed settings will be updated in the MQTT broker. This setting is only available if the controller is enabled, the Auto Discovery feature is available and enabled for the controller. This setting is not stored. Other controllers, like f.e. FHEM HTTP, do not support additional settings besides the checkbox to enable sending the data. diff --git a/docs/source/Plugin/P001_Door_switch.rst b/docs/source/Plugin/P001_Door_switch.rst index 731803b69..c8b464c8d 100644 --- a/docs/source/Plugin/P001_Door_switch.rst +++ b/docs/source/Plugin/P001_Door_switch.rst @@ -92,11 +92,8 @@ Advanced event management * **Long press min interval (ms)**: Same as above.. * **Use safe button (slower)**: Same as above.. -Data acquisition -^^^^^^^^^^^^^^^^ +.. include:: DataAcquisition.repl -* **Send to controller** 1..3: Check which controller (if any) you want to publish to. All or no controller can be used. -* **Interval**: How often should the task publish its value (optional for switch plugins). Indicators/values (recommended settings) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/source/Plugin/P001_PIR_sensor.rst b/docs/source/Plugin/P001_PIR_sensor.rst index f1039485b..68b1e610c 100644 --- a/docs/source/Plugin/P001_PIR_sensor.rst +++ b/docs/source/Plugin/P001_PIR_sensor.rst @@ -98,11 +98,8 @@ Advanced event management * **Long press min interval (ms)**: Same as above.. * **Use safe button (slower)**: Same as above.. -Data acquisition -^^^^^^^^^^^^^^^^ +.. include:: DataAcquisition.repl -* **Send to controller** 1..3: Check which controller (if any) you want to publish to. All or no controller can be used. -* **Interval**: How often should the task publish its value (optional for switch plugins). Indicators/values (recommended settings) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/source/Plugin/P001_Switch.rst b/docs/source/Plugin/P001_Switch.rst index ff810c710..faba7ddd6 100644 --- a/docs/source/Plugin/P001_Switch.rst +++ b/docs/source/Plugin/P001_Switch.rst @@ -110,11 +110,8 @@ Advanced event management * **Use safe button (slower)**: -Data acquisition -^^^^^^^^^^^^^^^^ +.. include:: DataAcquisition.repl -* **Send to controller** 1..3: Check which controller (if any) you want to publish to. All or no controller can be used. -* **Interval**: How often should the task publish its value (optional for switch plugins). Indicators/values (recommended settings) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/docs/source/Plugin/Task_config_page_Controllers_section.png b/docs/source/Plugin/Task_config_page_Controllers_section.png index d9333203e..50efecb47 100644 Binary files a/docs/source/Plugin/Task_config_page_Controllers_section.png and b/docs/source/Plugin/Task_config_page_Controllers_section.png differ diff --git a/docs/source/Tools/Tools.rst b/docs/source/Tools/Tools.rst index 6f7fd8d25..7145a6c9c 100644 --- a/docs/source/Tools/Tools.rst +++ b/docs/source/Tools/Tools.rst @@ -509,6 +509,16 @@ Depending on the internet connection, f.e. when connecting via a low-end mobile On ESP32, tasks like starting the MQTT connection can be delegated to an independent background task, to avoid blocking the normal working of ESPEasy, that will report the result when completed. This feature is enabled by default, as it is the preferred setting, but when this way of connecting is causing issues, it can be disabled. +MQTT Discover, Group incl. Taskname +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Added: 2026-08-14 + +To make the unique-id for Auto Discovery more unique when a Group is used, the Taskname can be inserted by enabling this option. Not enabled by default for backward compatibility with existing groups Auto-discovered by Home Assistant. + +Only enabled if MQTT Auto Discovery is included in the build. + + Allow OTA without size-check ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/src/DataStructs/SettingsStruct.h b/src/src/DataStructs/SettingsStruct.h index dc0b079f4..570a1503d 100644 --- a/src/src/DataStructs/SettingsStruct.h +++ b/src/src/DataStructs/SettingsStruct.h @@ -247,6 +247,11 @@ class SettingsStruct_tmpl inline void MQTTConnectInBackground(bool value) { VariousBits_2.MQTTConnectInBackground = !value; } #endif // if FEATURE_MQTT_CONNECT_BACKGROUND + #if FEATURE_MQTT_DISCOVER + inline bool MQTTDiscoverGroupInclTaskname() const { return VariousBits_1.MQTTDiscoverGroupInclTaskname; } + inline void MQTTDiscoverGroupInclTaskname(bool value) { VariousBits_1.MQTTDiscoverGroupInclTaskname = value; } + #endif // if FEATURE_MQTT_DISCOVER + // Flag indicating whether all task values should be sent in a single event or one event per task value (default behavior) bool CombineTaskValues_SingleEvent(taskIndex_t taskIndex) const; void CombineTaskValues_SingleEvent(taskIndex_t taskIndex, bool value); @@ -701,7 +706,7 @@ public: // VariousBits_1 defaults to 0, keep in mind when adding bit lookups. struct { - uint32_t unused_00 : 1; // Bit 00 + uint32_t MQTTDiscoverGroupInclTaskname : 1; // Bit 00 uint32_t appendUnitToHostname : 1; // Bit 01 Inverted uint32_t unused_02 : 1; // Bit 02 uniqueMQTTclientIdReconnect_unused uint32_t OldRulesEngine : 1; // Bit 03 Inverted diff --git a/src/src/Helpers/StringProvider.cpp b/src/src/Helpers/StringProvider.cpp index 1bd2c7a03..478be58a6 100644 --- a/src/src/Helpers/StringProvider.cpp +++ b/src/src/Helpers/StringProvider.cpp @@ -483,6 +483,12 @@ KeyValueStruct getKeyValue(LabelType::Enum label, bool extendedValue) return KeyValueStruct(F("MQTT Connect in background"), Settings.MQTTConnectInBackground()); } #endif // if FEATURE_MQTT_CONNECT_BACKGROUND + #if FEATURE_MQTT_DISCOVER + case LabelType::MQTT_DISCOVER_GROUP_INCL_TASKNAME: + { + return KeyValueStruct(F("MQTT Discover, Group incl. Taskname"), Settings.MQTTDiscoverGroupInclTaskname()); + } + #endif // if FEATURE_MQTT_DISCOVER #if FEATURE_COLORIZE_CONSOLE_LOGS case LabelType::COLORIZE_CONSOLE_LOGS: { diff --git a/src/src/Helpers/StringProvider.h b/src/src/Helpers/StringProvider.h index 4ab9a6894..b904824bf 100644 --- a/src/src/Helpers/StringProvider.h +++ b/src/src/Helpers/StringProvider.h @@ -112,6 +112,9 @@ struct LabelType { #if FEATURE_MQTT_CONNECT_BACKGROUND MQTT_CONNECT_IN_BACKGROUND, #endif // if FEATURE_MQTT_CONNECT_BACKGROUND + #if FEATURE_MQTT_DISCOVER + MQTT_DISCOVER_GROUP_INCL_TASKNAME, + #endif // if FEATURE_MQTT_DISCOVER #if FEATURE_COLORIZE_CONSOLE_LOGS COLORIZE_CONSOLE_LOGS, #endif diff --git a/src/src/Helpers/_CPlugin_Helper_mqtt.cpp b/src/src/Helpers/_CPlugin_Helper_mqtt.cpp index c9039a427..aebd0e550 100644 --- a/src/src/Helpers/_CPlugin_Helper_mqtt.cpp +++ b/src/src/Helpers/_CPlugin_Helper_mqtt.cpp @@ -9,9 +9,9 @@ # include "../Helpers/StringGenerator_System.h" # endif // if FEATURE_MQTT_DISCOVER # include "../Helpers/SystemVariables.h" -#if FEATURE_TASKVALUE_UNIT_OF_MEASURE -# include "../Helpers/ESPEasy_UnitOfMeasure.h" -#endif +# if FEATURE_TASKVALUE_UNIT_OF_MEASURE +# include "../Helpers/ESPEasy_UnitOfMeasure.h" +# endif # ifdef USES_P001 # include "../PluginStructs/P001_data_struct.h" @@ -372,45 +372,27 @@ bool getDiscoveryVType(struct EventStruct *event, QueryVType_ptr func_ptr, uint8 } // helper functions to supply a single value VType to be used by getDiscoveryVType -int Plugin_QueryVType_BinarySensor(uint8_t value_nr) { - return static_cast(Sensor_VType::SENSOR_TYPE_SWITCH) | Sensor_VType_CAN_SET; -} +int Plugin_QueryVType_BinarySensor(uint8_t value_nr) { return static_cast(Sensor_VType::SENSOR_TYPE_SWITCH) | Sensor_VType_CAN_SET; } int Plugin_QueryVType_BinarySensorInv(uint8_t value_nr) { return static_cast(Sensor_VType::SENSOR_TYPE_SWITCH_INVERTED) | Sensor_VType_CAN_SET; } -int Plugin_QueryVType_Analog(uint8_t value_nr) { - return static_cast(Sensor_VType::SENSOR_TYPE_ANALOG_ONLY); -} +int Plugin_QueryVType_Analog(uint8_t value_nr) { return static_cast(Sensor_VType::SENSOR_TYPE_ANALOG_ONLY); } -int Plugin_QueryVType_CO2(uint8_t value_nr) { - return static_cast(Sensor_VType::SENSOR_TYPE_CO2_ONLY); -} +int Plugin_QueryVType_CO2(uint8_t value_nr) { return static_cast(Sensor_VType::SENSOR_TYPE_CO2_ONLY); } -int Plugin_QueryVType_Distance(uint8_t value_nr) { - return static_cast(Sensor_VType::SENSOR_TYPE_DISTANCE_ONLY); -} +int Plugin_QueryVType_Distance(uint8_t value_nr) { return static_cast(Sensor_VType::SENSOR_TYPE_DISTANCE_ONLY); } -int Plugin_QueryVType_DustPM2_5(uint8_t value_nr) { - return static_cast(Sensor_VType::SENSOR_TYPE_DUSTPM2_5_ONLY); -} +int Plugin_QueryVType_DustPM2_5(uint8_t value_nr) { return static_cast(Sensor_VType::SENSOR_TYPE_DUSTPM2_5_ONLY); } -int Plugin_QueryVType_Lux(uint8_t value_nr) { - return static_cast(Sensor_VType::SENSOR_TYPE_LUX_ONLY); -} +int Plugin_QueryVType_Lux(uint8_t value_nr) { return static_cast(Sensor_VType::SENSOR_TYPE_LUX_ONLY); } -int Plugin_QueryVType_Temperature(uint8_t value_nr) { - return static_cast(Sensor_VType::SENSOR_TYPE_TEMP_ONLY); -} +int Plugin_QueryVType_Temperature(uint8_t value_nr) { return static_cast(Sensor_VType::SENSOR_TYPE_TEMP_ONLY); } -int Plugin_QueryVType_Weight(uint8_t value_nr) { - return static_cast(Sensor_VType::SENSOR_TYPE_WEIGHT_ONLY); -} +int Plugin_QueryVType_Weight(uint8_t value_nr) { return static_cast(Sensor_VType::SENSOR_TYPE_WEIGHT_ONLY); } -String makeHomeAssistantCompliantName(const String& name) { - return ESPEasy::net::makeRFCCompliantName(name, '_', '_', 0); -} +String makeHomeAssistantCompliantName(const String& name) { return ESPEasy::net::makeRFCCompliantName(name, '_', '_', 0); } # if FEATURE_MQTT_DEVICECLASS const char mqtt_binary_deviceclass_names[] PROGMEM = @@ -436,16 +418,17 @@ int MQTT_binary_deviceClassIndex(const String& deviceClassName) { // TwoWay devices are marked with ² in the selector, and discovered as 'light' instead of 'binary_sensor' bool MQTT_binary_deviceClassTwoWay(int devClassIndex) { - switch (devClassIndex) { // Index into mqtt_binary_deviceclass_names - case 1: // power - case 2: // light - case 3: // plug - case 5: // garage_door - case 8: // lock - case 26: // sound - case 28: // vibration - case 29: // switch - case 30: // outlet + switch (devClassIndex) // Index into mqtt_binary_deviceclass_names + { + case 1: // power + case 2: // light + case 3: // plug + case 5: // garage_door + case 8: // lock + case 26: // sound + case 28: // vibration + case 29: // switch + case 30: // outlet return true; default: break; @@ -455,9 +438,10 @@ bool MQTT_binary_deviceClassTwoWay(int devClassIndex) { // Switch devices are marked with ÷ in the selector, and discovered as 'switch' instead of 'light' bool MQTT_binary_deviceClassSwitch(int devClassIndex) { - switch (devClassIndex) { // Index into mqtt_binary_deviceclass_names - case 29: // switch - case 30: // outlet + switch (devClassIndex) // Index into mqtt_binary_deviceclass_names + { + case 29: // switch + case 30: // outlet return true; default: break; @@ -468,9 +452,11 @@ bool MQTT_binary_deviceClassSwitch(int devClassIndex) { # endif // if FEATURE_MQTT_DEVICECLASS # if FEATURE_MQTT_STATE_CLASS + const __FlashStringHelper* MQTT_sensor_StateClass(uint8_t index, bool display) { - switch (index) { + switch (index) + { case 0: return F(""); case 1: return display ? F("Measurement") : F("measurement"); case 2: return display ? F("Measurement-angle") : F("measurement_angle"); @@ -509,7 +495,8 @@ bool MQTT_SendAutoDiscovery(controllerIndex_t ControllerIndex, cpluginID_t CPlug // Dispatch autoDiscovery per supported CPlugin - switch (CPluginID) { + switch (CPluginID) + { case 5: // CPLUGIN_ID_005 : Home assistant/openHAB success = MQTT_HomeAssistant_SendAutoDiscovery(ControllerIndex, *ControllerSettings); break; @@ -638,7 +625,8 @@ bool MQTT_HomeAssistant_SendAutoDiscovery(controllerIndex_t ControllerIn struct EventStruct TempEvent(x); const uint8_t varCount = discoveryItems[s].varIndex + discoveryItems[s].valueCount; - switch (discoveryItems[s].VType) { + switch (discoveryItems[s].VType) + { // VType values to support, mapped to device classes: case Sensor_VType::SENSOR_TYPE_SWITCH: case Sensor_VType::SENSOR_TYPE_SWITCH_INVERTED: @@ -673,7 +661,7 @@ bool MQTT_HomeAssistant_SendAutoDiscovery(controllerIndex_t ControllerIn const String uom = MQTT_DiscoveryHelperGetValueUoM(x, v, discoveryItems[s]); if (discoveryItems[s].canSet) { - success &= MQTT_DiscoveryPublishWithStatusAndSet(x, v, valuename, + success &= MQTT_DiscoveryPublishWithStatusAndSet(v, valuename, ControllerIndex, ControllerSettings, F("device_automation"), @@ -687,7 +675,7 @@ bool MQTT_HomeAssistant_SendAutoDiscovery(controllerIndex_t ControllerIn useGroupId ? elementName : EMPTY_STRING, elementIds, true); // Send Trigger discovery } - success &= MQTT_DiscoveryPublishWithStatusAndSet(x, v, valuename, + success &= MQTT_DiscoveryPublishWithStatusAndSet(v, valuename, ControllerIndex, ControllerSettings, componentClass, @@ -731,7 +719,7 @@ bool MQTT_HomeAssistant_SendAutoDiscovery(controllerIndex_t ControllerIn # else // if FEATURE_MQTT_STATE_CLASS const String stateClass = EMPTY_STRING; # endif // if FEATURE_MQTT_STATE_CLASS - success &= MQTT_DiscoveryPublishWithStatusAndSet(x, v, valuename, + success &= MQTT_DiscoveryPublishWithStatusAndSet(v, valuename, ControllerIndex, ControllerSettings, F("sensor"), @@ -766,7 +754,7 @@ bool MQTT_HomeAssistant_SendAutoDiscovery(controllerIndex_t ControllerIn # else // if FEATURE_MQTT_STATE_CLASS const String stateClass = EMPTY_STRING; # endif // if FEATURE_MQTT_STATE_CLASS - success &= MQTT_DiscoveryPublishWithStatusAndSet(x, v, valuename, + success &= MQTT_DiscoveryPublishWithStatusAndSet(v, valuename, ControllerIndex, ControllerSettings, F("sensor"), @@ -805,7 +793,7 @@ bool MQTT_HomeAssistant_SendAutoDiscovery(controllerIndex_t ControllerIn # else // if FEATURE_MQTT_STATE_CLASS const String stateClass = EMPTY_STRING; # endif // if FEATURE_MQTT_STATE_CLASS - success &= MQTT_DiscoveryPublishWithStatusAndSet(x, v, valuename, + success &= MQTT_DiscoveryPublishWithStatusAndSet(v, valuename, ControllerIndex, ControllerSettings, F("sensor"), @@ -888,7 +876,7 @@ bool MQTT_HomeAssistant_SendAutoDiscovery(controllerIndex_t ControllerIn # else // if FEATURE_MQTT_STATE_CLASS const String stateClass = EMPTY_STRING; # endif // if FEATURE_MQTT_STATE_CLASS - success &= MQTT_DiscoveryPublishWithStatusAndSet(x, v, valuename, + success &= MQTT_DiscoveryPublishWithStatusAndSet(v, valuename, ControllerIndex, ControllerSettings, F("sensor"), @@ -1089,8 +1077,7 @@ bool MQTT_DiscoveryPublish(controllerIndex_t ControllerIndex, return result; } -bool MQTT_DiscoveryPublishWithStatusAndSet(taskIndex_t taskIndex, - uint8_t taskValue, +bool MQTT_DiscoveryPublishWithStatusAndSet(uint8_t taskValue, const String & valueName, controllerIndex_t ControllerIndex, ControllerSettingsStruct& ControllerSettings, @@ -1107,13 +1094,12 @@ bool MQTT_DiscoveryPublishWithStatusAndSet(taskIndex_t taskIndex, const String & elementId, bool sendTrigger) { if (!valueName.isEmpty()) { - // const String discoveryTopic(ControllerSettings.MqttAutoDiscoveryTopic); - // const String publishTopic(ControllerSettings.Publish); - - const String taskName = makeHomeAssistantCompliantName(getTaskDeviceName(taskIndex)); + const String taskName = makeHomeAssistantCompliantName(getTaskDeviceName(event->TaskIndex)); const String valName = makeHomeAssistantCompliantName(valueName); const String uniqueId = elementName.isEmpty() ? MQTT_TaskValueUniqueName(taskName, valName) + : Settings.MQTTDiscoverGroupInclTaskname() + ? strformat(F("%s_%s_%s"), elementId.c_str(), taskName.c_str(), valName.c_str()) : strformat(F("%s_%s"), elementId.c_str(), valName.c_str()); String message; { @@ -1171,7 +1157,7 @@ bool MQTT_DiscoveryPublishWithStatusAndSet(taskIndex_t taskIndex, return MQTT_DiscoveryPublish(ControllerIndex, topic, message, - taskIndex, + event->TaskIndex, taskValue, retainDsc); } diff --git a/src/src/Helpers/_CPlugin_Helper_mqtt.h b/src/src/Helpers/_CPlugin_Helper_mqtt.h index 5494fbc8b..28ae33fc1 100644 --- a/src/src/Helpers/_CPlugin_Helper_mqtt.h +++ b/src/src/Helpers/_CPlugin_Helper_mqtt.h @@ -91,8 +91,7 @@ bool MQTT_DiscoveryPublish(controllerIndex_t ControllerIndex, uint8_t v, bool retained = false); -bool MQTT_DiscoveryPublishWithStatusAndSet(taskIndex_t taskIndex, - uint8_t taskValue, +bool MQTT_DiscoveryPublishWithStatusAndSet(uint8_t taskValue, const String & valueName, controllerIndex_t ControllerIndex, ControllerSettingsStruct& ControllerSettings, diff --git a/src/src/WebServer/AdvancedConfigPage.cpp b/src/src/WebServer/AdvancedConfigPage.cpp index 47641f232..cb3a355b1 100644 --- a/src/src/WebServer/AdvancedConfigPage.cpp +++ b/src/src/WebServer/AdvancedConfigPage.cpp @@ -164,6 +164,9 @@ void handle_advanced() { #if FEATURE_MQTT_CONNECT_BACKGROUND Settings.MQTTConnectInBackground(isFormItemChecked(LabelType::MQTT_CONNECT_IN_BACKGROUND)); #endif // if FEATURE_MQTT_CONNECT_BACKGROUND + #if FEATURE_MQTT_DISCOVER + Settings.MQTTDiscoverGroupInclTaskname(isFormItemChecked(LabelType::MQTT_DISCOVER_GROUP_INCL_TASKNAME)); + #endif // if FEATURE_MQTT_DISCOVER #if FEATURE_COLORIZE_CONSOLE_LOGS Settings.ColorizeSerialLog(isFormItemChecked(LabelType::COLORIZE_CONSOLE_LOGS)); #endif @@ -341,6 +344,9 @@ void handle_advanced() { #if FEATURE_MQTT_CONNECT_BACKGROUND ,LabelType::MQTT_CONNECT_IN_BACKGROUND #endif // if FEATURE_MQTT_CONNECT_BACKGROUND + #if FEATURE_MQTT_DISCOVER + ,LabelType::MQTT_DISCOVER_GROUP_INCL_TASKNAME + #endif // if FEATURE_MQTT_DISCOVER #if FEATURE_COLORIZE_CONSOLE_LOGS ,LabelType::COLORIZE_CONSOLE_LOGS #endif