2018-01-18 15:19:28 +00:00
/*
2019-10-27 10:13:24 +00:00
xdrv_12_home_assistant . ino - home assistant support for Tasmota
2018-01-18 15:19:28 +00:00
2020-03-13 11:00:52 +00:00
Copyright ( C ) 2020 Erik Montnemery , Federico Leoni and Theo Arends
2018-01-18 15:19:28 +00:00
This program is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# ifdef USE_HOME_ASSISTANT
2020-02-06 02:29:44 +00:00
# define XDRV_12 12
// List of sensors ready for discovery
2020-02-06 16:32:54 +00:00
const char kHAssJsonSensorTypes [ ] PROGMEM =
2020-03-18 14:02:20 +00:00
D_JSON_TEMPERATURE " | " D_JSON_DEWPOINT " | " D_JSON_PRESSURE " | " D_JSON_PRESSUREATSEALEVEL " | "
2020-02-06 02:29:44 +00:00
D_JSON_APPARENT_POWERUSAGE " |Battery| " D_JSON_CURRENT " | " D_JSON_DISTANCE " | " D_JSON_FREQUENCY " | " D_JSON_HUMIDITY " | " D_JSON_ILLUMINANCE " | "
2020-05-12 18:06:35 +01:00
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 " | "
2020-03-18 14:02:20 +00:00
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 " | " ;
2020-05-06 18:52:08 +01:00
2020-02-06 16:32:54 +00:00
const char kHAssJsonSensorUnits [ ] PROGMEM =
2020-03-18 14:02:20 +00:00
" |||| "
2020-03-12 19:48:44 +00:00
" VA|%|A|Cm|Hz|%|LX| "
2020-05-12 18:06:35 +01:00
" %|ppd|ppd|ppd|ppd|ppd|ppd|µg/m³|µg/m³|µg/m³|Cos φ|W| | "
2020-03-12 19:48:44 +00:00
" VAr|kWh|kWh|V|Kg|kWh| "
" ppm|ppm|ppb| " ;
2020-05-06 18:52:08 +01:00
2020-02-06 16:32:54 +00:00
const char kHAssJsonSensorDevCla [ ] PROGMEM =
2020-03-18 14:02:20 +00:00
" dev_cla \" : \" temperature|ic \" : \" mdi:weather-rainy|dev_cla \" : \" pressure|dev_cla \" : \" pressure| "
2020-02-06 02:29:44 +00:00
" dev_cla \" : \" power|dev_cla \" : \" battery|ic \" : \" mdi:alpha-a-circle-outline|ic \" : \" mdi:leak|ic \" : \" mdi:current-ac|dev_cla \" : \" humidity|dev_cla \" : \" illuminance| "
" ic \" : \" mdi:cup-water|ic \" : \" mdi:flask|ic \" : \" mdi:flask|ic \" : \" mdi:flask|ic \" : \" mdi:flask|ic \" : \" mdi:flask|ic \" : \" mdi:flask| "
2020-05-12 18:06:35 +01:00
" 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| "
2020-03-18 14:02:20 +00:00
" 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:weather-windy|ic\":\"mdi:weather-windy|ic\":\"mdi:weather-windy|ic\":\"mdi:weather-windy|"
2020-02-06 16:32:54 +00:00
// List of sensors ready for discovery
2020-02-06 02:29:44 +00:00
const char HASS_DISCOVER_SENSOR [ ] PROGMEM =
2020-03-12 19:48:44 +00:00
" , \" unit_of_meas \" : \" %s \" , \" %s \" , " // unit of measure and class (or icon)
" \" frc_upd \" :true, " // force update for better graph representation
2020-03-18 14:02:20 +00:00
" \" val_tpl \" : \" {{value_json['%s']['%s'] " ; // "COUNTER":{"C1":0} -> {{ value_json['COUNTER']['C1']
2020-02-06 02:29:44 +00:00
const char HASS_DISCOVER_BASE [ ] PROGMEM =
2020-03-12 19:48:44 +00:00
" { \" 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
2018-11-07 09:30:03 +00:00
2020-02-06 16:32:54 +00:00
const char HASS_DISCOVER_RELAY [ ] PROGMEM =
2020-03-12 19:48:44 +00:00
" , \" cmd_t \" : \" %s \" , " // cmnd/dualr2/POWER2
" \" val_tpl \" : \" {{value_json.%s}} \" , " // POWER2
" \" pl_off \" : \" %s \" , " // OFF
" \" pl_on \" : \" %s \" " ; // ON
2018-12-04 20:31:23 +00:00
2020-03-12 19:48:44 +00:00
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
2018-12-04 20:31:23 +00:00
2020-03-14 21:36:19 +00:00
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.
2020-05-06 18:52:08 +01:00
const char HASS_DISCOVER_BASE_LIGHT [ ] PROGMEM =
2020-03-12 19:48:44 +00:00
" , \" 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)
2020-05-06 18:52:08 +01:00
" \" bri_val_tpl \" : \" {{value_json.%s}} \" " ;
2018-01-18 15:19:28 +00:00
2018-12-22 12:21:04 +00:00
const char HASS_DISCOVER_LIGHT_COLOR [ ] PROGMEM =
2020-03-12 19:48:44 +00:00
" , \" 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(',')}} \" " ;
2018-01-18 15:19:28 +00:00
2018-12-22 12:21:04 +00:00
const char HASS_DISCOVER_LIGHT_WHITE [ ] PROGMEM =
2020-03-12 19:48:44 +00:00
" , \" 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]}} \" " ;
2018-12-07 20:32:01 +00:00
2018-12-22 12:21:04 +00:00
const char HASS_DISCOVER_LIGHT_CT [ ] PROGMEM =
2020-03-12 19:48:44 +00:00
" , \" 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 " }} \" " ;
2018-11-28 19:43:36 +00:00
2018-12-22 12:21:04 +00:00
const char HASS_DISCOVER_LIGHT_SCHEME [ ] PROGMEM =
2020-03-12 19:48:44 +00:00
" , \" fx_cmd_t \" : \" %s \" , " // cmnd/led2/Scheme
" \" fx_stat_t \" : \" %s \" , " // stat/led2/RESULT
" \" fx_val_tpl \" : \" {{value_json. " D_CMND_SCHEME " }} \" , "
" \" fx_list \" :[ \" 0 \" , \" 1 \" , \" 2 \" , \" 3 \" , \" 4 \" ] " ; // string list with reference to scheme parameter.
2018-11-18 14:33:13 +00:00
2019-02-06 19:07:28 +00:00
const char HASS_DISCOVER_SENSOR_HASS_STATUS [ ] PROGMEM =
2020-03-12 19:48:44 +00:00
" , \" json_attr_t \" : \" %s \" , "
" \" unit_of_meas \" : \" %% \" , "
" \" val_tpl \" : \" {{value_json[' " D_JSON_RSSI " ']}} \" , "
" \" ic \" : \" mdi:information-outline \" " ;
2019-02-06 19:07:28 +00:00
2018-12-22 12:21:04 +00:00
const char HASS_DISCOVER_DEVICE_INFO [ ] PROGMEM =
2020-03-12 19:48:44 +00:00
" , \" uniq_id \" : \" %s \" , "
" \" dev \" :{ \" ids \" :[ \" %06X \" ], "
" \" name \" : \" %s \" , "
" \" mdl \" : \" %s \" , "
" \" sw \" : \" %s%s \" , "
" \" mf \" : \" Tasmota \" } " ;
2018-12-10 18:51:47 +00:00
2019-02-06 19:07:28 +00:00
const char HASS_DISCOVER_DEVICE_INFO_SHORT [ ] PROGMEM =
2020-03-12 19:48:44 +00:00
" , \" uniq_id \" : \" %s \" , "
" \" dev \" :{ \" ids \" :[ \" %06X \" ]} " ;
const char HASS_TRIGGER_TYPE [ ] PROGMEM =
" { \" atype \" : \" trigger \" , "
" \" t \" : \" %sT \" , "
" \" pl \" : \" { \\ \" TRIG \\ \" : \\ \" %s \\ \" } \" , "
" \" type \" : \" %s \" , "
" \" stype \" : \" %s \" , "
" \" dev \" :{ \" ids \" :[ \" %06X \" ]}} " ;
const char kHAssTriggerType [ ] PROGMEM =
" none|button_short_press|button_long_press|button_double_press " ;
2019-02-06 19:07:28 +00:00
2020-04-21 13:54:17 +01:00
const char kHAssTriggerTypeButtons [ ] PROGMEM =
" |button_short_press|button_double_press|button_triple_press|button_quadruple_press|button_quintuple_press|button_long_press| " ;
const char kHAssTriggerStringButtons [ ] PROGMEM =
" |SINGLE|DOUBLE|TRIPLE|QUAD|PENTA|HOLD| " ;
2020-05-06 18:52:08 +01:00
const char kHAssError1 [ ] PROGMEM =
2020-05-12 18:06:35 +01:00
" HASS: MQTT discovery failed due to too long topic or friendly name. Please shorten topic and/or friendly name. Failed to format " ;
2020-05-06 18:52:08 +01:00
const char kHAssError2 [ ] PROGMEM =
2020-05-12 18:06:35 +01:00
" 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 "
2020-05-06 18:52:08 +01:00
" The Relays have priority over the Lights, an incorrect order could lead to an erroneous Light control. \n "
" Please update your configuration to avoid inconsistent results. \n "
" Entities for Relays and Lights will not be available in Home Assistant until the configuration will be updated. " ;
2020-05-12 18:06:35 +01:00
const char kHAssError3 [ ] PROGMEM =
" HASS: Unable to create one or more entities from Json data, please check your configuration. Failed to parse " ;
2019-01-22 11:46:55 +00:00
uint8_t hass_init_step = 0 ;
uint8_t hass_mode = 0 ;
2019-02-06 19:07:28 +00:00
int hass_tele_period = 0 ;
2019-01-22 11:46:55 +00:00
2020-02-06 02:29:44 +00:00
void TryResponseAppend_P ( const char * format , . . . )
2019-02-06 19:07:28 +00:00
{
va_list args ;
va_start ( args , format ) ;
2019-03-11 14:41:06 +00:00
char dummy [ 2 ] ;
2019-03-24 13:23:20 +00:00
int dlen = vsnprintf_P ( dummy , 1 , format , args ) ;
int mlen = strlen ( mqtt_data ) ;
2020-02-06 02:29:44 +00:00
int slen = sizeof ( mqtt_data ) - 1 - mlen ;
if ( dlen > = slen )
{
2020-05-06 18:52:08 +01:00
AddLog_P2 ( LOG_LEVEL_ERROR , PSTR ( " %s (%u/%u): " ) , kHAssError1 , dlen , slen ) ;
2019-02-07 19:54:30 +00:00
va_start ( args , format ) ;
vsnprintf_P ( log_data , sizeof ( log_data ) , format , args ) ;
2019-02-06 19:07:28 +00:00
AddLog ( LOG_LEVEL_ERROR ) ;
2020-02-06 02:29:44 +00:00
}
else
{
2019-02-06 19:07:28 +00:00
va_start ( args , format ) ;
2019-03-24 13:23:20 +00:00
vsnprintf_P ( mqtt_data + mlen , slen , format , args ) ;
2019-02-06 19:07:28 +00:00
}
va_end ( args ) ;
}
2018-11-18 14:33:13 +00:00
void HAssAnnounceRelayLight ( void )
2018-01-18 15:19:28 +00:00
{
char stopic [ TOPSZ ] ;
2018-12-10 18:51:47 +00:00
char stemp1 [ TOPSZ ] ;
char stemp2 [ TOPSZ ] ;
char stemp3 [ TOPSZ ] ;
char unique_id [ 30 ] ;
2020-05-06 18:52:08 +01:00
bool LightControl = light_controller . isCTRGBLinked ( ) ; // SetOption37 - Color remapping for led channels, also provides an option for allowing independent handling of RGB and white channels
bool PwmMulti = Settings . flag3 . pwm_multi_channels ; // SetOption68 - Multi-channel PWM instead of a single light
bool is_topic_light = false ; // Switch HAss domain between Lights and Relays
bool ind_light = false ; // Controls Separated Lights when SetOption37 is >= 128
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
uint8_t dimmer = 1 ;
uint8_t max_lights = 1 ;
// 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
if ( PwmMulti ) { max_lights = Light . subtype ; }
if ( ! LightControl ) {
ind_light = 1 ;
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
2018-01-18 15:19:28 +00:00
2020-02-06 02:29:44 +00:00
for ( uint32_t i = 1 ; i < = MAX_RELAYS ; i + + )
{
2020-05-06 18:52:08 +01:00
bool RelayX = PinUsed ( GPIO_REL1 + i - 1 ) ;
is_topic_light = Settings . flag . hass_light & & RelayX | | light_type & & ! RelayX ; // SetOption30 - Enforce HAss autodiscovery as light
2018-01-18 15:19:28 +00:00
2020-02-06 02:29:44 +00:00
mqtt_data [ 0 ] = ' \0 ' ; // Clear retained message
2018-02-16 16:35:51 +00:00
2019-03-08 15:38:36 +00:00
// Clear "other" topic first in case the device has been reconfigured from light to switch or vice versa
2020-04-13 16:45:06 +01:00
snprintf_P ( unique_id , sizeof ( unique_id ) , PSTR ( " %06X_%s_%d " ) , ESP_getChipId ( ) , ( is_topic_light ) ? " RL " : " LI " , i ) ;
2019-02-06 19:07:28 +00:00
snprintf_P ( stopic , sizeof ( stopic ) , PSTR ( HOME_ASSISTANT_DISCOVERY_PREFIX " /%s/%s/config " ) ,
( is_topic_light ) ? " switch " : " light " , unique_id ) ;
2018-02-16 16:35:51 +00:00
MqttPublish ( stopic , true ) ;
2018-03-29 12:03:13 +01:00
// Clear or Set topic
2020-04-13 16:45:06 +01:00
snprintf_P ( unique_id , sizeof ( unique_id ) , PSTR ( " %06X_%s_%d " ) , ESP_getChipId ( ) , ( is_topic_light ) ? " LI " : " RL " , i ) ;
2019-02-06 19:07:28 +00:00
snprintf_P ( stopic , sizeof ( stopic ) , PSTR ( HOME_ASSISTANT_DISCOVERY_PREFIX " /%s/%s/config " ) ,
( is_topic_light ) ? " light " : " switch " , unique_id ) ;
2018-01-18 15:19:28 +00:00
2020-05-06 18:52:08 +01:00
if ( ( i < Light . device ) & & ! RelayX ) {
err_flag = true ;
AddLog_P2 ( LOG_LEVEL_ERROR , PSTR ( " %s " ) , kHAssError2 ) ;
} else {
if ( Settings . flag . hass_discovery & & ( RelayX | | ( Light . device > 0 ) & & ( max_lights > 0 ) ) & & ! err_flag )
{ // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
2020-05-17 17:42:14 +01:00
char name [ TOPSZ ] ; // friendlyname(33) + " " + index
2020-05-06 18:52:08 +01:00
char value_template [ 33 ] ;
char prefix [ TOPSZ ] ;
char * command_topic = stemp1 ;
char * state_topic = stemp2 ;
char * availability_topic = stemp3 ;
if ( i > MAX_FRIENDLYNAMES ) {
2020-05-17 17:42:14 +01:00
snprintf_P ( name , sizeof ( name ) , PSTR ( " %s %s %d " ) , SettingsText ( SET_DEVICENAME ) , SettingsText ( SET_FRIENDLYNAME1 ) , i - 1 ) ;
2020-05-06 18:52:08 +01:00
} else {
2020-05-17 17:42:14 +01:00
snprintf_P ( name , sizeof ( name ) , PSTR ( " %s %s " ) , SettingsText ( SET_DEVICENAME ) , SettingsText ( SET_FRIENDLYNAME1 + i - 1 ) ) ;
2020-05-06 18:52:08 +01:00
}
GetPowerDevice ( value_template , i , sizeof ( value_template ) , Settings . flag . device_index_enable ) ; // SetOption26 - Switch between POWER or POWER1
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 ) ;
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 )
# ifdef ESP8266
| | PWM_DIMMER = = my_module_type
# endif
)
2020-02-06 02:29:44 +00:00
{
2020-05-06 18:52:08 +01:00
if ( ! RelayX ) {
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 ] ;
if ( PwmMulti ) { // SetOption68 - Multi-channel PWM instead of a single light
snprintf_P ( channel_num , sizeof ( channel_num ) , PSTR ( " Channel%d " ) , i ) ;
} else {
2020-05-12 18:06:35 +01:00
if ( ! LightControl ) { // SetOption37 >= 128 - Color remapping for led channels, also provides an option for allowing independent handling of RGB and white channels
2020-05-06 18:52:08 +01:00
snprintf_P ( channel_num , sizeof ( channel_num ) , PSTR ( " " D_CMND_DIMMER " %d " ) , dimmer ) ;
dimmer + + ;
} else {
snprintf_P ( channel_num , sizeof ( channel_num ) , PSTR ( " " D_CMND_DIMMER " " ) ) ;
}
}
GetTopic_P ( brightness_command_topic , CMND , mqtt_topic , channel_num ) ;
TryResponseAppend_P ( HASS_DISCOVER_BASE_LIGHT , brightness_command_topic , state_topic , stemp3 , channel_num ) ;
}
if ( ( ind_light & & ! PwmMulti ) | | LightControl ) {
2018-01-18 15:19:28 +00:00
2020-05-06 18:52:08 +01:00
if ( Light . subtype > = LST_RGB ) {
char * rgb_command_topic = stemp1 ;
2018-12-07 20:32:01 +00:00
2020-05-06 18:52:08 +01:00
GetTopic_P ( rgb_command_topic , CMND , mqtt_topic , D_CMND_COLOR ) ;
TryResponseAppend_P ( HASS_DISCOVER_LIGHT_COLOR , rgb_command_topic , state_topic ) ;
2018-01-18 15:19:28 +00:00
2020-05-06 18:52:08 +01:00
char * effect_command_topic = stemp1 ;
GetTopic_P ( effect_command_topic , CMND , mqtt_topic , D_CMND_SCHEME ) ;
TryResponseAppend_P ( HASS_DISCOVER_LIGHT_SCHEME , effect_command_topic , state_topic ) ;
}
if ( LST_RGBW = = Light . subtype ) { wt_light = true ; }
if ( LST_RGBCW = = Light . subtype ) { ct_light = true ; }
}
if ( ( ! ind_light & & ct_light ) | | ( LST_COLDWARM = = Light . subtype & &
! PwmMulti & & LightControl ) ) {
char * color_temp_command_topic = stemp1 ;
GetTopic_P ( color_temp_command_topic , CMND , mqtt_topic , D_CMND_COLORTEMPERATURE ) ;
TryResponseAppend_P ( HASS_DISCOVER_LIGHT_CT , color_temp_command_topic , state_topic ) ;
ct_light = false ;
}
if ( ( ! ind_light & & wt_light ) | | ( LST_RGBW = = Light . subtype & &
! PwmMulti & & LightControl ) ) {
char * white_temp_command_topic = stemp1 ;
GetTopic_P ( white_temp_command_topic , CMND , mqtt_topic , D_CMND_WHITE ) ;
TryResponseAppend_P ( HASS_DISCOVER_LIGHT_WHITE , white_temp_command_topic , state_topic ) ;
wt_light = false ;
}
ind_light = false ;
max_lights - - ;
2018-01-18 15:19:28 +00:00
}
2020-05-06 18:52:08 +01:00
# endif // USE_LIGHT
TryResponseAppend_P ( PSTR ( " } " ) ) ;
2018-01-18 15:19:28 +00:00
}
}
MqttPublish ( stopic , true ) ;
}
}
2020-04-21 13:54:17 +01:00
void HAssAnnouncerTriggers ( uint8_t device , uint8_t present , uint8_t key , uint8_t toggle , uint8_t hold , uint8_t single , uint8_t trg_start , uint8_t trg_end )
2018-03-29 12:03:13 +01:00
{
2020-02-06 02:29:44 +00:00
// key 0 = button
// key 1 = switch
2018-12-04 20:31:23 +00:00
char stopic [ TOPSZ ] ;
2018-12-10 18:51:47 +00:00
char stemp1 [ TOPSZ ] ;
char stemp2 [ TOPSZ ] ;
char unique_id [ 30 ] ;
2020-04-21 13:54:17 +01:00
char trigger2 [ 8 ] ;
2018-12-04 20:31:23 +00:00
2020-02-06 02:29:44 +00:00
mqtt_data [ 0 ] = ' \0 ' ; // Clear retained message
2018-12-04 20:31:23 +00:00
2020-04-21 13:54:17 +01:00
for ( uint8_t i = trg_start ; i < = trg_end ; i + + ) {
GetTextIndexed ( trigger2 , sizeof ( trigger2 ) , i , kHAssTriggerStringButtons ) ;
snprintf_P ( unique_id , sizeof ( unique_id ) , PSTR ( " %06X_%s_%d_%s " ) , ESP_getChipId ( ) , key ? " SW " : " BTN " , device + 1 , key ? GetStateText ( i ) : trigger2 ) ;
2020-03-12 19:48:44 +00:00
snprintf_P ( stopic , sizeof ( stopic ) , PSTR ( HOME_ASSISTANT_DISCOVERY_PREFIX " /device_automation/%s/config " ) , unique_id ) ;
if ( Settings . flag . hass_discovery & & present ) { // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
2020-05-17 17:42:14 +01:00
char name [ TOPSZ ] ; // friendlyname(33) + " " + "BTN" + " " + index
2020-03-12 19:48:44 +00:00
char value_template [ 33 ] ;
char prefix [ TOPSZ ] ;
char * state_topic = stemp1 ;
char * availability_topic = stemp2 ;
char jsoname [ 8 ] ;
GetPowerDevice ( value_template , device + 1 , sizeof ( value_template ) , key + Settings . flag . device_index_enable ) ; // Force index for Switch 1, Index on Button1 is controlled by SetOption26 - Switch between POWER or POWER1
snprintf_P ( jsoname , sizeof ( jsoname ) , PSTR ( " %s%d " ) , key ? " SWITCH " : " BUTTON " , device + 1 ) ;
GetTopic_P ( state_topic , STAT , mqtt_topic , jsoname ) ;
GetTopic_P ( availability_topic , TELE , mqtt_topic , S_LWT ) ;
char param [ 21 ] ;
char subtype [ 9 ] ;
uint8_t pload = toggle ;
2020-04-21 13:54:17 +01:00
if ( key ) {
if ( ( i = = 2 & & toggle ! = 0 ) | | ( i = = 3 & & hold ! = 0 ) ) {
if ( i = = 3 ) { pload = hold ; }
GetTextIndexed ( param , sizeof ( param ) , pload , kHAssTriggerType ) ;
snprintf_P ( subtype , sizeof ( subtype ) , PSTR ( " switch_%d " ) , device + 1 ) ;
Response_P ( HASS_TRIGGER_TYPE , state_topic , GetStateText ( i ) , param , subtype , ESP_getChipId ( ) ) ;
} else { mqtt_data [ 0 ] = ' \0 ' ; } // Need to be cleaned again to avoid duplicate
} else {
char trigger1 [ 24 ] ;
GetTextIndexed ( trigger1 , sizeof ( trigger1 ) , i , kHAssTriggerTypeButtons ) ;
snprintf_P ( subtype , sizeof ( subtype ) , PSTR ( " button_%d " ) , device + 1 ) ;
if ( i > 1 & & single ) {
mqtt_data [ 0 ] = ' \0 ' ; // Need to be cleaned again to avoid duplicate
} else {
Response_P ( HASS_TRIGGER_TYPE , state_topic , trigger2 , trigger1 , subtype , ESP_getChipId ( ) ) ;
}
}
2020-03-12 19:48:44 +00:00
}
MqttPublish ( stopic , true ) ;
}
}
2020-03-14 21:36:19 +00:00
void HAssAnnouncerBinSensors ( uint8_t device , uint8_t present , uint8_t dual , uint8_t toggle , uint8_t pir )
2020-03-12 19:48:44 +00:00
{
char stopic [ TOPSZ ] ;
char stemp1 [ TOPSZ ] ;
char stemp2 [ TOPSZ ] ;
char unique_id [ 30 ] ;
mqtt_data [ 0 ] = ' \0 ' ; // Clear retained message
2020-04-13 16:45:06 +01:00
snprintf_P ( unique_id , sizeof ( unique_id ) , PSTR ( " %06X_SW_%d " ) , ESP_getChipId ( ) , device + 1 ) ;
2018-12-10 18:51:47 +00:00
snprintf_P ( stopic , sizeof ( stopic ) , PSTR ( HOME_ASSISTANT_DISCOVERY_PREFIX " /binary_sensor/%s/config " ) , unique_id ) ;
2018-12-04 20:31:23 +00:00
2020-03-12 19:48:44 +00:00
if ( Settings . flag . hass_discovery & & present ) { // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
if ( ! toggle | | dual ) {
2020-05-17 17:42:14 +01:00
char name [ TOPSZ ] ; // friendlyname(33) + " " + "BTN" + " " + index
2020-03-12 19:48:44 +00:00
char value_template [ 33 ] ;
char prefix [ TOPSZ ] ;
char * state_topic = stemp1 ;
char * availability_topic = stemp2 ;
char jsoname [ 8 ] ;
2019-10-24 22:29:06 +01:00
2020-03-12 19:48:44 +00:00
GetPowerDevice ( value_template , device + 1 , sizeof ( value_template ) , 1 + Settings . flag . device_index_enable ) ; // Force index for Switch 1, Index on Button1 is controlled by SetOption26 - Switch between POWER or POWER1
snprintf_P ( jsoname , sizeof ( jsoname ) , PSTR ( " SWITCH%d " ) , device + 1 ) ;
GetTopic_P ( state_topic , STAT , mqtt_topic , jsoname ) ;
GetTopic_P ( availability_topic , TELE , mqtt_topic , S_LWT ) ;
2020-02-06 16:32:54 +00:00
2020-05-17 17:42:14 +01:00
snprintf_P ( name , sizeof ( name ) , PSTR ( " %s Switch%d " ) , SettingsText ( SET_DEVICENAME ) , device + 1 ) ;
2020-03-12 19:48:44 +00:00
Response_P ( HASS_DISCOVER_BASE , name , state_topic , availability_topic ) ;
2020-03-14 21:36:19 +00:00
if ( ! pir ) {
TryResponseAppend_P ( HASS_DISCOVER_BIN_SWITCH , PSTR ( D_RSLT_STATE ) , SettingsText ( SET_STATE_TXT2 ) , SettingsText ( SET_STATE_TXT1 ) ) ;
} else {
TryResponseAppend_P ( HASS_DISCOVER_BIN_PIR , PSTR ( D_RSLT_STATE ) , SettingsText ( SET_STATE_TXT2 ) ) ;
}
2020-04-13 16:45:06 +01:00
TryResponseAppend_P ( HASS_DISCOVER_DEVICE_INFO_SHORT , unique_id , ESP_getChipId ( ) ) ;
2020-03-12 19:48:44 +00:00
TryResponseAppend_P ( PSTR ( " } " ) ) ;
2019-11-15 01:37:30 +00:00
}
2018-12-04 20:31:23 +00:00
}
MqttPublish ( stopic , true ) ;
}
void HAssAnnounceSwitches ( void )
{
2020-03-12 19:48:44 +00:00
for ( uint32_t switch_index = 0 ; switch_index < MAX_SWITCHES ; switch_index + + )
2020-02-06 02:29:44 +00:00
{
2020-03-12 19:48:44 +00:00
uint8_t switch_present = 0 ;
uint8_t dual = 0 ;
uint8_t toggle = 1 ;
uint8_t hold = 0 ;
2020-03-14 21:36:19 +00:00
uint8_t pir = 0 ;
2020-03-12 19:48:44 +00:00
2020-04-27 11:54:07 +01:00
if ( PinUsed ( GPIO_SWT1 , switch_index ) ) { switch_present = 1 ; }
2020-03-13 11:00:52 +00:00
2020-05-06 18:52:08 +01:00
if ( KeyTopicActive ( 1 ) & & strcmp ( SettingsText ( SET_MQTT_SWITCH_TOPIC ) , mqtt_topic ) ) // Enable Discovery for Switches only if SwitchTopic is set to a custom name
2020-02-06 02:29:44 +00:00
{
2018-12-04 20:31:23 +00:00
2020-03-12 19:48:44 +00:00
// switch matrix for triggers and binary sensor generation when switchtopic is set as custom (default index is 0,0 - TOGGLE, TOGGLE):
// SWITCHMODE INTERNAL BINARY PRESS DOUBLE PRESS HOLD T,H
// 0 TOGGLE NO TOGGLE (button_short_press) NONE NONE 1,0
// 1 FOLLOW YES NONE NONE NONE 0,0
// 2 FOLLOW_INV YES NONE NONE NONE 0,0
// 3 PUSHBUTTON YES TOGGLE (button_short_press) NONE NONE 1,0
// 4 PUSHBUTTON_INV YES TOGGLE (button_short_press) NONE NONE 1,0
// 5 PUSHBUTTONHOLD YES TOGGLE (button_short_press) NONE HOLD (button_long_press) 1,2
// 6 PUSHBUTTONHOLD_INV YES TOGGLE (button_short_press) NONE HOLD (button_long_press) 1,2
// 7 PUSHBUTTON_TOGGLE NO TOGGLE (button_short_press) NONE NONE 1,0
// 8 TOGGLEMULTI NO TOGGLE (button_short_press) HOLD (button_double_press) NONE 1,3
// 9 FOLLOWMULTI YES NONE HOLD (button_double_press) NONE 0,3
// 10 FOLLOWMULTI_INV YES NONE HOLD (button_double_press) NONE 0,3
// 11 PUSHHOLDMULTI NO TOGGLE (button_short_press) NONE INC_DEC (button_long_press) 1,0
// INV (not available) CLEAR (not available)
// 12 PUSHHOLDMULTI_INV NO TOGGLE (button_short_press) NONE CLEAR (button_long_press) 1,0
// INV (not available) INC_DEC (not available)
2020-03-18 14:02:20 +00:00
// 13 PUSHON YES NONE NONE NONE 0,0
// 14 PUSHON_INV YES NONE NONE NONE 0,0
2020-03-12 19:48:44 +00:00
// Please note: SwitchMode11 and 12 will register just TOGGLE (button_short_press)
2020-03-13 11:00:52 +00:00
2020-03-12 19:48:44 +00:00
// Trigger types: "0 = none | 1 = button_short_press | 2 = button_long_press | 3 = button_double_press";
uint8_t swmode = Settings . switchmode [ switch_index ] ;
switch ( swmode ) {
2020-03-15 10:41:24 +00:00
case FOLLOW :
case FOLLOW_INV :
2020-03-12 19:48:44 +00:00
toggle = 0 ; // Binary sensor and no triggers
break ;
2020-03-15 10:41:24 +00:00
case PUSHBUTTON :
case PUSHBUTTON_INV :
2020-03-12 19:48:44 +00:00
dual = 1 ; // Binary sensor and TOGGLE (button_short_press) trigger
break ;
2020-03-15 10:41:24 +00:00
case PUSHBUTTONHOLD :
case PUSHBUTTONHOLD_INV :
2020-03-12 19:48:44 +00:00
dual = 1 ; // Binary sensor, TOGGLE (button_short_press) and HOLD (button_long_press) triggers
hold = 2 ;
break ;
2020-03-15 10:41:24 +00:00
case TOGGLEMULTI :
2020-03-12 19:48:44 +00:00
hold = 3 ; // TOGGLE (button_short_press) and HOLD (button_double_press) triggers
break ;
2020-03-15 10:41:24 +00:00
case FOLLOWMULTI :
case FOLLOWMULTI_INV :
2020-03-12 19:48:44 +00:00
dual = 1 ; // Binary sensor and HOLD (button_long_press) trigger
toggle = 0 ;
hold = 3 ;
break ;
2020-03-15 10:41:24 +00:00
case PUSHON :
case PUSHON_INV :
2020-03-14 21:36:19 +00:00
toggle = 0 ;
2020-04-21 13:54:17 +01:00
pir = 1 ; // Binary sensor with only ON state and automatic OFF after 1 second
2018-12-04 20:31:23 +00:00
}
2020-03-13 11:00:52 +00:00
2020-03-12 19:48:44 +00:00
} else { switch_present = 0 ; }
2018-12-04 20:31:23 +00:00
2020-04-21 13:54:17 +01:00
HAssAnnouncerTriggers ( switch_index , switch_present , 1 , toggle , hold , 0 , 2 , 3 ) ;
2020-03-14 21:36:19 +00:00
HAssAnnouncerBinSensors ( switch_index , switch_present , dual , toggle , pir ) ;
2018-12-04 20:31:23 +00:00
}
}
void HAssAnnounceButtons ( void )
{
2020-03-12 19:48:44 +00:00
for ( uint32_t button_index = 0 ; button_index < MAX_KEYS ; button_index + + )
2020-02-06 02:29:44 +00:00
{
2020-03-12 19:48:44 +00:00
uint8_t button_present = 0 ;
2020-04-21 13:54:17 +01:00
uint8_t single = 0 ;
2018-03-29 12:03:13 +01:00
2020-04-10 17:24:08 +01:00
# ifdef ESP8266
2020-03-12 19:48:44 +00:00
if ( ! button_index & & ( ( SONOFF_DUAL = = my_module_type ) | | ( CH4 = = my_module_type ) ) )
{
button_present = 1 ;
2020-04-10 17:24:08 +01:00
} else
# endif
{
2020-04-27 11:54:07 +01:00
if ( PinUsed ( GPIO_KEY1 , button_index ) ) {
2018-03-29 12:03:13 +01:00
button_present = 1 ;
2020-02-06 02:29:44 +00:00
}
2020-03-12 19:48:44 +00:00
}
2018-03-29 12:03:13 +01:00
2020-04-21 13:54:17 +01:00
// Button matrix for triggers generation when SetOption73 is enabled:
// N SetOption1 SetOption11 SetOption13 PRESS MULTI PRESS HOLD
// 1 0 0 0 SINGLE (10 - button_short_press) DOUBLE to PENTA YES (button_long_press)
// 2 1 0 0 SINGLE (10 - button_short_press) DOUBLE to PENTA YES (button_long_press)
// 3 0 1 0 DOUBLE (11 - button_short_press) SINGLE then TRIPLE TO PENTA YES (button_long_press)
// 4 1 1 0 DOUBLE (11 - button_short_press) SINGLE then TRIPLE TO PENTA YES (button_long_press)
// 5 0 0 1 SINGLE (10 - button_short_press) NONE NONE
// 6 1 0 1 SINGLE (10 - button_short_press) NONE NONE
// 7 0 1 1 SINGLE (10 - button_short_press) NONE NONE
// 8 1 1 1 SINGLE (10 - button_short_press) NONE NONE
// Trigger types: 10 = button_short_press | 11 = button_double_press | 12 = button_triple_press | 13 = button_quadruple_press | 14 = button_quintuple_press | 3 = button_long_press
2020-05-06 18:52:08 +01:00
if ( ! Settings . flag3 . mqtt_buttons ) { // Enable Buttons for discovery [SetOption73] - Decouple button from relay and send just mqtt topic
2020-04-21 13:54:17 +01:00
button_present = 0 ;
} else {
if ( Settings . flag . button_single ) { // [SetOption13] Immediate action on button press, just SINGLE trigger
single = 1 ;
}
2020-03-12 19:48:44 +00:00
}
2020-04-21 13:54:17 +01:00
HAssAnnouncerTriggers ( button_index , button_present , 0 , 0 , 0 , single , 1 , 6 ) ;
2018-03-29 12:03:13 +01:00
}
}
2020-03-18 14:02:20 +00:00
void HAssAnnounceSensor ( const char * sensorname , const char * subsensortype , const char * MultiSubName , uint8_t subqty , uint8_t subidx , uint8_t nested , const char * SubKey )
2018-11-18 14:33:13 +00:00
{
char stopic [ TOPSZ ] ;
2018-12-10 18:51:47 +00:00
char stemp1 [ TOPSZ ] ;
char stemp2 [ TOPSZ ] ;
char unique_id [ 30 ] ;
2020-03-18 14:02:20 +00:00
char subname [ 20 ] ;
2018-11-18 14:33:13 +00:00
2020-02-06 02:29:44 +00:00
mqtt_data [ 0 ] = ' \0 ' ; // Clear retained message
2020-04-10 17:24:08 +01:00
// Clear or Set topic
2020-03-18 14:02:20 +00:00
NoAlNumToUnderscore ( subname , MultiSubName ) ; //Replace all non alphaumeric characters to '_' to avoid topic name issues
2020-04-13 16:45:06 +01:00
snprintf_P ( unique_id , sizeof ( unique_id ) , PSTR ( " %06X_%s_%s " ) , ESP_getChipId ( ) , sensorname , subname ) ;
2020-03-18 14:02:20 +00:00
snprintf_P ( stopic , sizeof ( stopic ) , PSTR ( HOME_ASSISTANT_DISCOVERY_PREFIX " /sensor/%s/config " ) , unique_id ) ;
2020-02-06 02:29:44 +00:00
if ( Settings . flag . hass_discovery )
{ // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
2020-05-17 17:42:14 +01:00
char name [ TOPSZ ] ; // friendlyname(33) + " " + sensorname(20?) + " " + sensortype(20?)
2018-11-18 14:33:13 +00:00
char prefix [ TOPSZ ] ;
2018-12-10 18:51:47 +00:00
char * state_topic = stemp1 ;
char * availability_topic = stemp2 ;
2018-11-18 14:33:13 +00:00
2020-02-06 02:29:44 +00:00
GetTopic_P ( state_topic , TELE , mqtt_topic , PSTR ( D_RSLT_SENSOR ) ) ;
2020-05-17 17:42:14 +01:00
snprintf_P ( name , sizeof ( name ) , PSTR ( " %s %s %s " ) , SettingsText ( SET_DEVICENAME ) , sensorname , MultiSubName ) ;
2018-11-18 14:33:13 +00:00
GetTopic_P ( availability_topic , TELE , mqtt_topic , S_LWT ) ;
2018-12-22 12:15:55 +00:00
2020-02-06 02:29:44 +00:00
Response_P ( HASS_DISCOVER_BASE , name , state_topic , availability_topic ) ;
2020-04-13 16:45:06 +01:00
TryResponseAppend_P ( HASS_DISCOVER_DEVICE_INFO_SHORT , unique_id , ESP_getChipId ( ) ) ;
2020-03-12 19:48:44 +00:00
2020-02-06 16:32:54 +00:00
2020-02-06 02:29:44 +00:00
char jname [ 32 ] ;
2020-03-18 14:02:20 +00:00
int sensor_index = GetCommandCode ( jname , sizeof ( jname ) , SubKey , kHAssJsonSensorTypes ) ;
2020-02-06 02:29:44 +00:00
if ( sensor_index > - 1 ) {
2020-03-18 14:02:20 +00:00
2020-02-06 02:29:44 +00:00
char param1 [ 20 ] ;
2020-02-06 16:32:54 +00:00
GetTextIndexed ( param1 , sizeof ( param1 ) , sensor_index , kHAssJsonSensorUnits ) ;
2020-02-06 02:29:44 +00:00
switch ( sensor_index ) {
2020-03-18 14:02:20 +00:00
case 0 : // Temperature and DewPoint
case 1 :
2020-02-06 02:29:44 +00:00
snprintf_P ( param1 , sizeof ( param1 ) , PSTR ( " °%c " ) , TempUnit ( ) ) ; // C or F
break ;
2020-03-18 14:02:20 +00:00
case 2 : // Pressure and Sea Level Pressure
case 3 :
2020-02-06 02:29:44 +00:00
snprintf_P ( param1 , sizeof ( param1 ) , PSTR ( " %s " ) , PressureUnit ( ) . c_str ( ) ) ;
break ;
2020-04-10 17:24:08 +01:00
// case 4: // Speed. Default to km/h if not set to have a graph representation under HAss
2020-03-18 14:02:20 +00:00
// case 5:
// case 6:
// case 7:
// if (Settings.flag2.speed_conversion == 0) {
// snprintf_P(param1, sizeof(param1), PSTR("km/h"));
// } else {
// snprintf_P(param1, sizeof(param1), PSTR("%s"), SpeedUnit().c_str());
// }
// break;
}
2020-02-06 02:29:44 +00:00
char param2 [ 50 ] ;
2020-02-06 16:32:54 +00:00
GetTextIndexed ( param2 , sizeof ( param2 ) , sensor_index , kHAssJsonSensorDevCla ) ;
2020-02-06 02:29:44 +00:00
TryResponseAppend_P ( HASS_DISCOVER_SENSOR , param1 , param2 , sensorname , subsensortype ) ;
2020-03-18 14:02:20 +00:00
2020-02-06 02:29:44 +00:00
if ( subidx ) {
TryResponseAppend_P ( PSTR ( " [%d] " ) , subqty - 1 ) ;
}
} else {
TryResponseAppend_P ( HASS_DISCOVER_SENSOR , " " , " ic \" : \" mdi:eye " , sensorname , subsensortype ) ;
2018-11-18 14:33:13 +00:00
}
2020-03-18 14:02:20 +00:00
if ( nested ) {
TryResponseAppend_P ( PSTR ( " ['%s'] " ) , SubKey ) ;
}
2020-02-06 02:29:44 +00:00
TryResponseAppend_P ( PSTR ( " }} \" } " ) ) ;
2018-11-18 14:33:13 +00:00
}
MqttPublish ( stopic , true ) ;
}
void HAssAnnounceSensors ( void )
{
uint8_t hass_xsns_index = 0 ;
2020-02-06 02:29:44 +00:00
do
{
2018-11-18 14:33:13 +00:00
mqtt_data [ 0 ] = ' \0 ' ;
int tele_period_save = tele_period ;
2020-02-06 02:29:44 +00:00
tele_period = 2 ; // Do not allow HA updates during next function call
XsnsNextCall ( FUNC_JSON_APPEND , hass_xsns_index ) ; // ,"INA219":{"Voltage":4.494,"Current":0.020,"Power":0.089}
2018-11-18 14:33:13 +00:00
tele_period = tele_period_save ;
2020-02-06 02:29:44 +00:00
char sensordata [ 512 ] ; // Copy because we need to write to mqtt_data
2018-11-18 14:33:13 +00:00
strlcpy ( sensordata , mqtt_data , sizeof ( sensordata ) ) ;
2020-02-06 02:29:44 +00:00
if ( strlen ( sensordata ) )
{
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
2020-03-18 14:02:20 +00:00
//snprintf_P(sensordata, sizeof(sensordata), PSTR("{\"HX711\":{\"Weight\":[22,34,1023.4]}}"));
2020-03-14 21:36:19 +00:00
2020-05-12 18:06:35 +01:00
2019-01-22 11:46:55 +00:00
StaticJsonBuffer < 500 > jsonBuffer ;
2020-02-06 02:29:44 +00:00
JsonObject & root = jsonBuffer . parseObject ( sensordata ) ;
if ( ! root . success ( ) )
{
2020-05-12 18:06:35 +01:00
AddLog_P2 ( LOG_LEVEL_ERROR , PSTR ( " %s '%s' " ) , kHAssError3 , sensordata ) ;
2018-11-18 14:33:13 +00:00
continue ;
}
2020-02-06 02:29:44 +00:00
for ( auto sensor : root )
{
const char * sensorname = sensor . key ;
JsonObject & sensors = sensor . value . as < JsonObject > ( ) ;
if ( ! sensors . success ( ) )
{
2020-05-12 18:06:35 +01:00
AddLog_P2 ( LOG_LEVEL_ERROR , PSTR ( " %s '%s' " ) , kHAssError3 , sensordata ) ;
2018-11-18 14:33:13 +00:00
continue ;
}
2020-03-18 14:02:20 +00:00
2020-02-06 02:29:44 +00:00
for ( auto subsensor : sensors )
{
2020-03-18 14:02:20 +00:00
if ( subsensor . value . is < JsonObject & > ( ) ) {
// If there is a nested json on sensor data, second level entitites will be created
char NestedName [ 20 ] ;
char NewSensorName [ 20 ] ;
snprintf_P ( NestedName , sizeof ( NestedName ) , PSTR ( " %s " ) , subsensor . key ) ;
JsonObject & subsensors = subsensor . value . as < JsonObject > ( ) ;
for ( auto subsensor : subsensors ) {
snprintf_P ( NewSensorName , sizeof ( NewSensorName ) , PSTR ( " %s %s " ) , NestedName , subsensor . key ) ;
HAssAnnounceSensor ( sensorname , NestedName , NewSensorName , 0 , 0 , 1 , subsensor . key ) ;
2020-04-10 17:24:08 +01:00
}
} else if ( subsensor . value . is < JsonArray & > ( ) ) {
2020-03-18 14:02:20 +00:00
// If there is more than a value on sensor data, 'n' entitites will be created
2020-02-06 02:29:44 +00:00
JsonArray & subsensors = subsensor . value . as < JsonArray & > ( ) ;
2020-03-18 14:02:20 +00:00
uint8_t subqty = subsensors . size ( ) ;
2020-02-06 02:29:44 +00:00
char MultiSubName [ 20 ] ;
for ( int i = 1 ; i < = subqty ; i + + ) {
2020-03-18 14:02:20 +00:00
snprintf_P ( MultiSubName , sizeof ( MultiSubName ) , PSTR ( " %s %d " ) , subsensor . key , i ) ;
HAssAnnounceSensor ( sensorname , subsensor . key , MultiSubName , i , 1 , 0 , subsensor . key ) ;
2020-02-06 02:29:44 +00:00
}
2020-03-18 14:02:20 +00:00
} else { HAssAnnounceSensor ( sensorname , subsensor . key , subsensor . key , 0 , 0 , 0 , subsensor . key ) ; }
2018-11-18 14:33:13 +00:00
}
}
}
2019-01-08 14:25:12 +00:00
yield ( ) ;
2018-11-18 14:33:13 +00:00
} while ( hass_xsns_index ! = 0 ) ;
}
2020-04-21 13:54:17 +01:00
void HAssAnnounceDeviceInfoAndStatusSensor ( void )
2019-02-06 19:07:28 +00:00
{
char stopic [ TOPSZ ] ;
char stemp1 [ TOPSZ ] ;
char stemp2 [ TOPSZ ] ;
char unique_id [ 30 ] ;
// Announce sensor
2020-02-06 02:29:44 +00:00
mqtt_data [ 0 ] = ' \0 ' ; // Clear retained message
2019-02-06 19:07:28 +00:00
// Clear or Set topic
2020-04-13 16:45:06 +01:00
snprintf_P ( unique_id , sizeof ( unique_id ) , PSTR ( " %06X_status " ) , ESP_getChipId ( ) ) ;
2019-02-06 19:07:28 +00:00
snprintf_P ( stopic , sizeof ( stopic ) , PSTR ( HOME_ASSISTANT_DISCOVERY_PREFIX " /sensor/%s/config " ) , unique_id ) ;
2020-02-06 02:29:44 +00:00
if ( Settings . flag . hass_discovery )
{ // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
2020-05-17 17:42:14 +01:00
char name [ TOPSZ ] ; // friendlyname(33) + " " + "status"
2019-02-06 19:07:28 +00:00
char prefix [ TOPSZ ] ;
char * state_topic = stemp1 ;
char * availability_topic = stemp2 ;
2020-05-17 17:42:14 +01:00
snprintf_P ( name , sizeof ( name ) , PSTR ( " %s status " ) , SettingsText ( SET_DEVICENAME ) ) ;
2019-02-06 19:07:28 +00:00
GetTopic_P ( state_topic , TELE , mqtt_topic , PSTR ( D_RSLT_HASS_STATE ) ) ;
GetTopic_P ( availability_topic , TELE , mqtt_topic , S_LWT ) ;
2020-02-06 02:29:44 +00:00
Response_P ( HASS_DISCOVER_BASE , name , state_topic , availability_topic ) ;
2019-03-24 13:23:20 +00:00
TryResponseAppend_P ( HASS_DISCOVER_SENSOR_HASS_STATUS , state_topic ) ;
2020-05-17 17:42:14 +01:00
TryResponseAppend_P ( HASS_DISCOVER_DEVICE_INFO , unique_id , ESP_getChipId ( ) , SettingsText ( SET_DEVICENAME ) ,
2020-03-13 10:14:03 +00:00
ModuleName ( ) . c_str ( ) , my_version , my_image ) ;
2019-03-24 13:23:20 +00:00
TryResponseAppend_P ( PSTR ( " } " ) ) ;
2019-02-06 19:07:28 +00:00
}
MqttPublish ( stopic , true ) ;
}
void HAssPublishStatus ( void )
{
2020-05-06 18:52:08 +01:00
Response_P ( PSTR ( " { \" " D_JSON_VERSION " \" : \" %s%s \" , \" " D_JSON_BUILDDATETIME " \" : \" %s \" , \" " D_CMND_MODULE " or " D_CMND_TEMPLATE " \" : \" %s \" , "
" \" " D_JSON_RESTARTREASON " \" : \" %s \" , \" " D_JSON_UPTIME " \" : \" %s \" , \" " D_CMND_HOSTNAME " \" : \" %s \" , "
" \" " 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 ( ) , my_hostname , WiFi . localIP ( ) . toString ( ) . c_str ( ) , WifiGetRssiAsQuality ( WiFi . RSSI ( ) ) ,
WiFi . RSSI ( ) , WifiLinkCount ( ) , WifiDowntime ( ) . c_str ( ) , MqttConnectCount ( ) , loop_load_avg ) ;
2019-02-06 19:07:28 +00:00
MqttPublishPrefixTopic_P ( TELE , PSTR ( D_RSLT_HASS_STATE ) ) ;
}
2019-01-22 11:46:55 +00:00
void HAssDiscovery ( void )
2018-03-29 12:03:13 +01:00
{
// Configure Tasmota for default Home Assistant parameters to keep discovery message as short as possible
2020-02-06 02:29:44 +00:00
if ( Settings . flag . hass_discovery )
2020-03-12 19:48:44 +00:00
{ // SetOption19 - Control Home Assistant automatic discovery (See SetOption59)
Settings . flag . mqtt_response = 0 ; // SetOption4 - Switch between MQTT RESULT or COMMAND - Response always as RESULT and not as uppercase command
Settings . flag . decimal_text = 1 ; // SetOption17 - Switch between decimal or hexadecimal output - Respond with decimal color values
Settings . flag3 . hass_tele_on_power = 1 ; // SetOption59 - Send tele/%topic%/STATE in addition to stat/%topic%/RESULT - send tele/STATE message as stat/RESULT
// the purpose of that is so that if HA is restarted, state in HA will be correct within one teleperiod otherwise state
// will not be correct until the device state is changed this is why in the patterns for switch and light, we tell HA to trigger on STATE, not RESULT.
Settings . light_scheme = 0 ; // To just control color it needs to be Scheme 0
2018-03-29 12:03:13 +01:00
}
2020-02-06 02:29:44 +00:00
if ( Settings . flag . hass_discovery | | ( 1 = = hass_mode ) )
{ // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
2018-11-18 14:33:13 +00:00
2018-03-29 13:01:38 +01:00
// Send info about buttons
2018-12-04 20:31:23 +00:00
HAssAnnounceButtons ( ) ;
2018-11-18 14:33:13 +00:00
2018-12-04 20:31:23 +00:00
// Send info about switches
HAssAnnounceSwitches ( ) ;
2018-03-29 12:03:13 +01:00
2018-11-18 14:33:13 +00:00
// Send info about sensors
HAssAnnounceSensors ( ) ;
2019-02-06 19:07:28 +00:00
2020-05-06 18:52:08 +01:00
// Send info about relays and lights
HAssAnnounceRelayLight ( ) ;
2019-02-06 19:07:28 +00:00
// Send info about status sensor
2020-04-21 13:54:17 +01:00
HAssAnnounceDeviceInfoAndStatusSensor ( ) ;
2018-03-29 13:01:38 +01:00
}
2018-03-29 12:03:13 +01:00
}
2019-01-22 11:46:55 +00:00
void HAssDiscover ( void )
2018-08-23 15:05:51 +01:00
{
2020-02-06 02:29:44 +00:00
hass_mode = 1 ; // Force discovery
hass_init_step = 1 ; // Delayed discovery
2018-08-23 15:05:51 +01:00
}
2019-10-24 08:47:28 +01:00
void HAssAnyKey ( void )
{
2020-02-06 02:29:44 +00:00
if ( ! Settings . flag . hass_discovery )
{
return ;
} // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
2019-10-24 08:47:28 +01:00
2020-03-04 17:02:27 +00:00
uint32_t key = ( XdrvMailbox . payload > > 16 ) & 0xFF ; // 0 = Button, 1 = Switch
uint32_t device = XdrvMailbox . payload & 0xFF ; // Device number or 1 if more Buttons than Devices
2020-04-21 13:54:17 +01:00
uint32_t state = ( XdrvMailbox . payload > > 8 ) & 0xFF ; // 0 = Off, 1 = On, 2 = Toggle, 3 = Hold, 10,11,12,13 and 14 for Button Multipress
2020-03-04 17:02:27 +00:00
2020-03-12 19:48:44 +00:00
if ( ! key & & KeyTopicActive ( 0 ) ) { // Button and ButtonTopic is active
2020-03-04 17:02:27 +00:00
device = ( XdrvMailbox . payload > > 24 ) & 0xFF ; // Button number
}
2019-10-24 08:47:28 +01:00
char scommand [ CMDSZ ] ;
2020-03-12 19:48:44 +00:00
char sw_topic [ TOPSZ ] ;
char key_topic [ TOPSZ ] ;
2020-04-21 13:54:17 +01:00
char trg_state [ 8 ] ;
2020-03-12 19:48:44 +00:00
char * tmpbtn = SettingsText ( SET_MQTT_BUTTON_TOPIC ) ;
char * tmpsw = SettingsText ( SET_MQTT_SWITCH_TOPIC ) ;
uint8_t evkey = 0 ; // Flag to select the correct topic for a trigger or a binary_sensor
Format ( sw_topic , tmpsw , sizeof ( sw_topic ) ) ;
Format ( key_topic , tmpbtn , sizeof ( key_topic ) ) ;
2020-04-21 13:54:17 +01:00
if ( state > = 2 ) { evkey = 1 ; }
2020-03-12 19:48:44 +00:00
snprintf_P ( scommand , sizeof ( scommand ) , PSTR ( " %s%d%s " ) , ( key ) ? " SWITCH " : " BUTTON " , device , ( evkey ) ? " T " : " " ) ;
2019-10-24 08:47:28 +01:00
char stopic [ TOPSZ ] ;
2020-02-06 16:32:54 +00:00
2020-05-12 18:06:35 +01:00
if ( state = = 3 ) {
2020-04-21 13:54:17 +01:00
snprintf_P ( trg_state , sizeof ( trg_state ) , GetStateText ( 3 ) ) ;
} else {
2020-05-12 18:06:35 +01:00
if ( state = = 2 ) { state = 10 ; }
2020-04-21 13:54:17 +01:00
GetTextIndexed ( trg_state , sizeof ( trg_state ) , state - 9 , kHAssTriggerStringButtons ) ;
}
2019-11-15 01:37:30 +00:00
GetTopic_P ( stopic , STAT , mqtt_topic , scommand ) ;
2020-04-21 13:54:17 +01:00
Response_P ( S_JSON_COMMAND_SVALUE , ( evkey ) ? " TRIG " : PSTR ( D_RSLT_STATE ) , ( key ) ? GetStateText ( state ) : trg_state ) ;
2019-10-27 10:13:24 +00:00
MqttPublish ( stopic ) ;
2019-10-24 08:47:28 +01:00
}
2018-01-18 15:19:28 +00:00
/*********************************************************************************************\
* Interface
\ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2019-01-28 13:08:33 +00:00
bool Xdrv12 ( uint8_t function )
2018-01-18 15:19:28 +00:00
{
2019-01-28 13:08:33 +00:00
bool result = false ;
2018-01-18 15:19:28 +00:00
2020-02-06 02:29:44 +00:00
if ( Settings . flag . mqtt_enabled )
{ // SetOption3 - Enable MQTT
switch ( function )
{
case FUNC_EVERY_SECOND :
if ( hass_init_step )
{
hass_init_step - - ;
if ( ! hass_init_step )
{
HAssDiscovery ( ) ; // Scheduled discovery using available resources
2019-01-22 11:46:55 +00:00
}
2020-02-06 02:29:44 +00:00
}
else if ( Settings . flag . hass_discovery & & Settings . tele_period )
{ // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
hass_tele_period + + ;
if ( hass_tele_period > = Settings . tele_period )
{
hass_tele_period = 0 ;
mqtt_data [ 0 ] = ' \0 ' ;
HAssPublishStatus ( ) ;
}
}
break ;
case FUNC_ANY_KEY :
HAssAnyKey ( ) ;
break ;
case FUNC_MQTT_INIT :
hass_mode = 0 ; // Discovery only if Settings.flag.hass_discovery is set
hass_init_step = 2 ; // Delayed discovery
break ;
2018-01-18 15:19:28 +00:00
}
}
return result ;
}
2020-05-07 16:58:39 +01:00
# endif // USE_HOME_ASSISTANT