From 0d6aaa64b5c21f8704ca390bc369f39bb4a49d25 Mon Sep 17 00:00:00 2001 From: Federico Leoni Date: Thu, 18 Jun 2020 14:43:18 -0300 Subject: [PATCH] Discovery Update --- tasmota/my_user_config.h | 2 + tasmota/xdrv_12_home_assistant.ino | 139 ++++++++++++++++++----------- 2 files changed, 90 insertions(+), 51 deletions(-) diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 87647a98d2ff..4f5623de19c3 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -140,6 +140,8 @@ // -- MQTT - Home Assistant Discovery ------------- #define HOME_ASSISTANT_DISCOVERY_ENABLE false // [SetOption19] Home Assistant Discovery (false = Disable, true = Enable) #define HASS_AS_LIGHT false // [SetOption30] Enforce HAss autodiscovery as light +//#define DEEPSLEEP_LWT_HA_DISCOVERY // Enable LWT topic and its payloads for read-only sensors (Status sensor not included) and binary_sensors on HAss Discovery (Commented out: all read-only sensors and binary_sensors + // won't be shown as OFFLINE on Home Assistant when the device is DeepSleeping - NOTE: This is only for read-only sensors and binary_sensors, relays will be shown as OFFLINE) // -- MQTT - Options ------------------------------ #define MQTT_RESULT_COMMAND false // [SetOption4] Switch between MQTT RESULT or COMMAND diff --git a/tasmota/xdrv_12_home_assistant.ino b/tasmota/xdrv_12_home_assistant.ino index 70ab6d62063b..476d457651a6 100644 --- a/tasmota/xdrv_12_home_assistant.ino +++ b/tasmota/xdrv_12_home_assistant.ino @@ -27,14 +27,14 @@ const char kHAssJsonSensorTypes[] PROGMEM = D_JSON_APPARENT_POWERUSAGE "|Battery|" D_JSON_CURRENT "|" D_JSON_DISTANCE "|" D_JSON_FREQUENCY "|" D_JSON_HUMIDITY "|" D_JSON_ILLUMINANCE "|" D_JSON_MOISTURE "|PB0.3|PB0.5|PB1|PB2.5|PB5|PB10|PM1|PM2.5|PM10|" D_JSON_POWERFACTOR "|" D_JSON_POWERUSAGE "|" D_JSON_TOTAL_START_TIME "|" D_JSON_REACTIVE_POWERUSAGE "|" D_JSON_TODAY "|" D_JSON_TOTAL "|" D_JSON_VOLTAGE "|" D_JSON_WEIGHT "|" D_JSON_YESTERDAY "|" - D_JSON_CO2 "|" D_JSON_ECO2 "|" D_JSON_TVOC "|"; + D_JSON_CO2 "|" D_JSON_ECO2 "|" D_JSON_TVOC "|" D_COLOR_RED "|" D_COLOR_GREEN "|" D_COLOR_BLUE"|" D_CCT "|" D_PROXIMITY "|Ambient|"; const char kHAssJsonSensorUnits[] PROGMEM = "||||" "VA|%|A|Cm|Hz|%|LX|" "%|ppd|ppd|ppd|ppd|ppd|ppd|µg/m³|µg/m³|µg/m³|Cos φ|W| |" "VAr|kWh|kWh|V|Kg|kWh|" - "ppm|ppm|ppb|"; + "ppm|ppm|ppb|R|G|B|" D_UNIT_KELVIN "| |LX|"; const char kHAssJsonSensorDevCla[] PROGMEM = "dev_cla\":\"temperature|ic\":\"mdi:weather-rainy|dev_cla\":\"pressure|dev_cla\":\"pressure|" @@ -42,61 +42,64 @@ const char kHAssJsonSensorDevCla[] PROGMEM = "ic\":\"mdi:cup-water|ic\":\"mdi:flask|ic\":\"mdi:flask|ic\":\"mdi:flask|ic\":\"mdi:flask|ic\":\"mdi:flask|ic\":\"mdi:flask|" "ic\":\"mdi:air-filter|ic\":\"mdi:air-filter|ic\":\"mdi:air-filter|ic\":\"mdi:alpha-f-circle-outline|dev_cla\":\"power|ic\":\"mdi:progress-clock|" "dev_cla\":\"power|dev_cla\":\"power|dev_cla\":\"power|ic\":\"mdi:alpha-v-circle-outline|ic\":\"mdi:scale|dev_cla\":\"power|" - "ic\":\"mdi:periodic-table-co2|ic\":\"mdi:air-filter|ic\":\"mdi:periodic-table-co2|"; + "ic\":\"mdi:periodic-table-co2|ic\":\"mdi:air-filter|ic\":\"mdi:periodic-table-co2|" + "ic\":\"mdi:palette|ic\":\"mdi:palette|ic\":\"mdi:palette|ic\":\"mdi:temperature-kelvin|ic\":\"mdi:ruler|dev_cla\":\"illuminance|"; //"ic\":\"mdi:weather-windy|ic\":\"mdi:weather-windy|ic\":\"mdi:weather-windy|ic\":\"mdi:weather-windy|" // List of sensors ready for discovery +const char HASS_DISCOVER_BASE[] PROGMEM = + "{\"name\":\"%s\"," // dualr2 1 + "\"stat_t\":\"%s\""; // stat/dualr2/RESULT (implies "\"optimistic\":\"false\",") + const char HASS_DISCOVER_SENSOR[] PROGMEM = - ",\"unit_of_meas\":\"%s\",\"%s\"," // unit of measure and class (or icon) - "\"frc_upd\":true," // force update for better graph representation - "\"val_tpl\":\"{{value_json['%s']['%s']"; // "COUNTER":{"C1":0} -> {{ value_json['COUNTER']['C1'] + ",\"unit_of_meas\":\"%s\",\"%s\"," // unit of measure and class (or icon) + "\"frc_upd\":true," // force update for better graph representation + "\"val_tpl\":\"{{value_json['%s']['%s']"; // "COUNTER":{"C1":0} -> {{ value_json['COUNTER']['C1'] -const char HASS_DISCOVER_BASE[] PROGMEM = - "{\"name\":\"%s\"," // dualr2 1 - "\"stat_t\":\"%s\"," // stat/dualr2/RESULT (implies "\"optimistic\":\"false\",") - "\"avty_t\":\"%s\"," // tele/dualr2/LWT - "\"pl_avail\":\"" D_ONLINE "\"," // Online - "\"pl_not_avail\":\"" D_OFFLINE "\""; // Offline +const char HASS_DISCOVER_SENSOR_LWT[] PROGMEM = + ",\"avty_t\":\"%s\"," // tele/dualr2/LWT + "\"pl_avail\":\"" D_ONLINE "\"," // Online + "\"pl_not_avail\":\"" D_OFFLINE "\""; // Offline const char HASS_DISCOVER_RELAY[] PROGMEM = - ",\"cmd_t\":\"%s\"," // cmnd/dualr2/POWER2 - "\"val_tpl\":\"{{value_json.%s}}\"," // POWER2 - "\"pl_off\":\"%s\"," // OFF - "\"pl_on\":\"%s\""; // ON + ",\"cmd_t\":\"%s\"," // cmnd/dualr2/POWER2 + "\"val_tpl\":\"{{value_json.%s}}\"," // POWER2 + "\"pl_off\":\"%s\"," // OFF + "\"pl_on\":\"%s\""; // ON const char HASS_DISCOVER_BIN_SWITCH[] PROGMEM = - ",\"val_tpl\":\"{{value_json.%s}}\"," // STATE - "\"frc_upd\":true," // In ON/OFF case, enable force_update to make automations work - "\"pl_on\":\"%s\"," // ON - "\"pl_off\":\"%s\""; // OFF + ",\"val_tpl\":\"{{value_json.%s}}\"," // STATE + "\"frc_upd\":true," // In ON/OFF case, enable force_update to make automations work + "\"pl_on\":\"%s\"," // ON + "\"pl_off\":\"%s\""; // OFF const char HASS_DISCOVER_BIN_PIR[] PROGMEM = - ",\"val_tpl\":\"{{value_json.%s}}\"," // STATE - "\"frc_upd\":true," // In ON/OFF case, enable force_update to make automations work - "\"pl_on\":\"%s\"," // ON - "\"off_dly\":1"; // Switchmode13 and Switchmode14 doesn't transmit an OFF state. + ",\"val_tpl\":\"{{value_json.%s}}\"," // STATE + "\"frc_upd\":true," // In ON/OFF case, enable force_update to make automations work + "\"pl_on\":\"%s\"," // ON + "\"off_dly\":1"; // Switchmode13 and Switchmode14 doesn't transmit an OFF state. const char HASS_DISCOVER_BASE_LIGHT[] PROGMEM = - ",\"bri_cmd_t\":\"%s\"," // cmnd/led2/Dimmer - "\"bri_stat_t\":\"%s\"," // stat/led2/RESULT - "\"bri_scl\":100," // 100% - "\"on_cmd_type\":\"%s\"," // power on (first), power on (last), no power on (brightness) + ",\"bri_cmd_t\":\"%s\"," // cmnd/led2/Dimmer + "\"bri_stat_t\":\"%s\"," // stat/led2/RESULT + "\"bri_scl\":100," // 100% + "\"on_cmd_type\":\"%s\"," // power on (first), power on (last), no power on (brightness) "\"bri_val_tpl\":\"{{value_json.%s}}\""; const char HASS_DISCOVER_LIGHT_COLOR[] PROGMEM = - ",\"rgb_cmd_t\":\"%s2\"," // cmnd/led2/Color2 - "\"rgb_stat_t\":\"%s\"," // stat/led2/RESULT + ",\"rgb_cmd_t\":\"%s2\"," // cmnd/led2/Color2 + "\"rgb_stat_t\":\"%s\"," // stat/led2/RESULT "\"rgb_val_tpl\":\"{{value_json." D_CMND_COLOR ".split(',')[0:3]|join(',')}}\""; const char HASS_DISCOVER_LIGHT_WHITE[] PROGMEM = - ",\"whit_val_cmd_t\":\"%s\"," // cmnd/led2/White - "\"whit_val_stat_t\":\"%s\"," // stat/led2/RESULT + ",\"whit_val_cmd_t\":\"%s\"," // cmnd/led2/White + "\"whit_val_stat_t\":\"%s\"," // stat/led2/RESULT "\"whit_val_scl\":100," "\"whit_val_tpl\":\"{{value_json.Channel[3]}}\""; const char HASS_DISCOVER_LIGHT_CT[] PROGMEM = - ",\"clr_temp_cmd_t\":\"%s\"," // cmnd/led2/CT - "\"clr_temp_stat_t\":\"%s\"," // stat/led2/RESULT + ",\"clr_temp_cmd_t\":\"%s\"," // cmnd/led2/CT + "\"clr_temp_stat_t\":\"%s\"," // stat/led2/RESULT "\"clr_temp_val_tpl\":\"{{value_json." D_CMND_COLORTEMPERATURE "}}\""; const char HASS_DISCOVER_LIGHT_SCHEME[] PROGMEM = @@ -141,7 +144,7 @@ const char kHAssTriggerStringButtons[] PROGMEM = "|SINGLE|DOUBLE|TRIPLE|QUAD|PENTA|HOLD|"; const char kHAssError1[] PROGMEM = - "HASS: MQTT discovery failed due to too long topic or friendly name. Please shorten topic and/or friendly name. Failed to format"; + "HASS: MQTT discovery failed due to too long topic or device/friendly name. Please shorten topic and/or device/friendly name. Failed to format"; const char kHAssError2[] PROGMEM = "HASS: The configuration of the Relays is wrong, there is a Light that is using an index higher than the number of the validated relay.\n " @@ -195,13 +198,22 @@ void HAssAnnounceRelayLight(void) bool ct_light = false; // Controls a CT Light when SetOption37 is >= 128 bool wt_light = false; // Controls a White Light when SetOption37 is >= 128 bool err_flag = false; // When true it blocks the creation of entities if the order of the Relays is not correct to avoid issue with Lights + bool TuyaMod = false; // Controls Tuya MCU modules bool PwmMod = false; // Controls PWM_DIMMER module + bool FanMod = false; // Controls SONOFF_IFAN0X modules uint8_t dimmer = 1; + uint8_t valid_relay = 0; uint8_t max_lights = 1; + uint8_t TuyaRel = 0; + uint8_t TuyaRelInv = 0; + uint8_t TuyaDim = 0; #ifdef ESP8266 - if (PWM_DIMMER == my_module_type) { PwmMod = true; } + if (PWM_DIMMER == my_module_type ) { PwmMod = true; } // + if (SONOFF_IFAN02 == my_module_type || SONOFF_IFAN03 == my_module_type) { FanMod = true; } + if (SONOFF_DUAL == my_module_type) { valid_relay = 2; } + if (TUYA_DIMMER == my_module_type || SK03_TUYA == my_module_type) { TuyaMod = true; } #endif //ESP8266 // If there is a special Light to be enabled and managed with SetOption68 or SetOption37 >= 128, Discovery calculates the maximum number of entities to be generated in advance @@ -209,17 +221,21 @@ void HAssAnnounceRelayLight(void) if (PwmMulti) { max_lights = Light.subtype; } if (!LightControl) { - ind_light = 1; + ind_light = true; if (!PwmMulti) { max_lights = 2;} } - // Lights types: 0 = LST_NONE / 1 = LST_SINGLE / 2 = LST_COLDWARM / 3 = LST_RGB / 4 = LST_RGBW / 5 = LST_RGBCW - for (uint32_t i = 1; i <= MAX_RELAYS; i++) { - bool RelayX = PinUsed(GPIO_REL1 +i-1); - is_topic_light = Settings.flag.hass_light && RelayX || light_type && !RelayX || PwmMod; // SetOption30 - Enforce HAss autodiscovery as light +#ifdef USE_TUYA_MCU + TuyaRel = TuyaGetDpId((TUYA_MCU_FUNC_REL1+ i-1) + active_device - 1); + TuyaRelInv = TuyaGetDpId((TUYA_MCU_FUNC_REL1_INV+ i-1) + active_device - 1); + TuyaDim = TuyaGetDpId((TUYA_MCU_FUNC_DIMMER) + active_device - 1); +#endif //USE_TUYA_MCU + + bool RelayX = PinUsed(GPIO_REL1 +i-1) || (valid_relay >= i) || (TuyaRel > 0 && TuyaMod) || (TuyaRelInv > 0 && TuyaMod); // Check if the gpio is configured as Relay or force it for Sonoff DUAL R1 with MCU and Tuya MCU + is_topic_light = Settings.flag.hass_light && RelayX || light_type && !RelayX || PwmMod || (TuyaDim > 0 && TuyaMod); // SetOption30 - Enforce HAss autodiscovery as light mqtt_data[0] = '\0'; // Clear retained message // Clear "other" topic first in case the device has been reconfigured from light to switch or vice versa @@ -255,13 +271,14 @@ void HAssAnnounceRelayLight(void) GetTopic_P(command_topic, CMND, mqtt_topic, value_template); GetTopic_P(state_topic, TELE, mqtt_topic, D_RSLT_STATE); GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); - Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic); + Response_P(HASS_DISCOVER_BASE, name, state_topic); + TryResponseAppend_P(HASS_DISCOVER_SENSOR_LWT, availability_topic); TryResponseAppend_P(HASS_DISCOVER_RELAY, command_topic, value_template, SettingsText(SET_STATE_TXT1), SettingsText(SET_STATE_TXT2)); TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP_getChipId()); #ifdef USE_LIGHT if (i >= Light.device) { - if (!RelayX || PwmMod) { + if (!RelayX || PwmMod || (TuyaDim > 0 && TuyaMod)) { char *brightness_command_topic = stemp1; strncpy_P(stemp3, Settings.flag.not_power_linked ? PSTR("last") : PSTR("brightness"), sizeof(stemp3)); // SetOption20 - Control power in relation to Dimmer/Color/Ct changes char channel_num[9]; @@ -411,6 +428,14 @@ void HAssAnnouncerBinSensors(uint8_t device, uint8_t present, uint8_t dual, uint TryResponseAppend_P(HASS_DISCOVER_BIN_PIR, PSTR(D_RSLT_STATE), SettingsText(SET_STATE_TXT2)); } TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP_getChipId()); +#ifdef DEEPSLEEP_LWT_HA_DISCOVERY + TryResponseAppend_P(HASS_DISCOVER_SENSOR_LWT, availability_topic); +#else + if (Settings.deepsleep == 0) + { + TryResponseAppend_P(HASS_DISCOVER_SENSOR_LWT, availability_topic); + } +#endif //DEEPSLEEP_LWT_HA_DISCOVERY TryResponseAppend_P(PSTR("}")); } } @@ -505,7 +530,7 @@ void HAssAnnounceButtons(void) { button_present = 1; } else -#endif +#endif // ESP8266 { if (PinUsed(GPIO_KEY1, button_index)) { button_present = 1; @@ -557,12 +582,21 @@ void HAssAnnounceSensor(const char *sensorname, const char *subsensortype, const char prefix[TOPSZ]; char *state_topic = stemp1; char *availability_topic = stemp2; + //bool LwtSensor = MQTT_LWT_DISCOVERY; GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_SENSOR)); snprintf_P(name, sizeof(name), PSTR("%s %s %s"), SettingsText(SET_DEVICENAME), sensorname, MultiSubName); GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); - Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic); + Response_P(HASS_DISCOVER_BASE, name, state_topic); + #ifdef DEEPSLEEP_LWT_HA_DISCOVERY + TryResponseAppend_P(HASS_DISCOVER_SENSOR_LWT, availability_topic); +#else + if (Settings.deepsleep == 0) + { + TryResponseAppend_P(HASS_DISCOVER_SENSOR_LWT, availability_topic); + } +#endif //DEEPSLEEP_LWT_HA_DISCOVERY TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO_SHORT, unique_id, ESP_getChipId()); @@ -629,8 +663,8 @@ void HAssAnnounceSensors(void) sensordata[0] = '{'; snprintf_P(sensordata, sizeof(sensordata), PSTR("%s}"), sensordata); // {"INA219":{"Voltage":4.494,"Current":0.020,"Power":0.089}} // USE THE FOLLOWING LINE TO TEST JSON - //snprintf_P(sensordata, sizeof(sensordata), PSTR("{\"HX711\":{\"Weight\":[22,34,1023.4]}}")); - + //snprintf_P(sensordata, sizeof(sensordata), PSTR("{\"APDS9960\":{\"Red\":282,\"Green\":252,\"Blue\":196,\"Ambient\":169,\"CCT\":4217,\"Proximity\":9}}")); + //snprintf_P(sensordata, sizeof(sensordata), PSTR("{\"ENERGY\":{\"TotalStartTime\":\"2018-11-23T15:33:47\",\"Total\":0.017,\"TotalTariff\":[0.000,0.017],\"Yesterday\":0.000,\"Today\":0.002,\"ExportActive\":0.000,\"ExportTariff\":[0.000,0.000],\"Period\":0.00,\"Power\":0.00,\"ApparentPower\":7.84,\"ReactivePower\":-7.21,\"Factor\":0.39,\"Frequency\":50.0,\"Voltage\":234.31,\"Current\":0.039,\"ImportActive\":12.580,\"ImportReactive\":0.002,\"ExportReactive\":39.131,\"PhaseAngle\":290.45}}")); StaticJsonBuffer<500> jsonBuffer; JsonObject &root = jsonBuffer.parseObject(sensordata); @@ -703,7 +737,8 @@ void HAssAnnounceDeviceInfoAndStatusSensor(void) GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_HASS_STATE)); GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT); - Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic); + Response_P(HASS_DISCOVER_BASE, name, state_topic); + TryResponseAppend_P(HASS_DISCOVER_SENSOR_LWT, availability_topic); TryResponseAppend_P(HASS_DISCOVER_SENSOR_HASS_STATUS, state_topic); TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO, unique_id, ESP_getChipId(), SettingsText(SET_DEVICENAME), ModuleName().c_str(), my_version, my_image); @@ -719,7 +754,7 @@ void HAssPublishStatus(void) "\"" D_CMND_IPADDRESS "\":\"%s\",\"" D_JSON_RSSI "\":\"%d\",\"" D_JSON_SIGNAL " (dBm)""\":\"%d\"," "\"WiFi " D_JSON_LINK_COUNT "\":%d,\"WiFi " D_JSON_DOWNTIME "\":\"%s\",\"" D_JSON_MQTT_COUNT "\":%d,\"LoadAvg\":%lu}"), my_version, my_image, GetBuildDateAndTime().c_str(), ModuleName().c_str(), GetResetReason().c_str(), - GetUptime().c_str(), NetworkHostname(), WiFi.localIP().toString().c_str(), WifiGetRssiAsQuality(WiFi.RSSI()), + GetUptime().c_str(), my_hostname, WiFi.localIP().toString().c_str(), WifiGetRssiAsQuality(WiFi.RSSI()), WiFi.RSSI(), WifiLinkCount(), WifiDowntime().c_str(), MqttConnectCount(), loop_load_avg); MqttPublishPrefixTopic_P(TELE, PSTR(D_RSLT_HASS_STATE)); } @@ -812,7 +847,6 @@ void HAssAnyKey(void) bool Xdrv12(uint8_t function) { bool result = false; - if (Settings.flag.mqtt_enabled) { // SetOption3 - Enable MQTT switch (function) @@ -844,10 +878,13 @@ bool Xdrv12(uint8_t function) case FUNC_MQTT_INIT: hass_mode = 0; // Discovery only if Settings.flag.hass_discovery is set hass_init_step = 2; // Delayed discovery + // if (!Settings.flag.hass_discovery) { + // AddLog_P2(LOG_LEVEL_INFO, PSTR("MQT: homeassistant/49A3BC/Discovery = {\"dev\":{\"ids\":[\"49A3BC\"]},\"cmd_t\":\"cmnd/test1/\",\"Discovery\":0}")); + // } break; } } return result; } -#endif // USE_HOME_ASSISTANT +#endif // USE_HOME_ASSISTANT \ No newline at end of file