#pragma once /* mqtt functions for gateway */ // this device (gateway) definition to be able to create devices in HA #define CREATE_GW_MQTT_DEVICE \ dev = config.createNestedObject("device"); \ dev["ids"]=MQTT_DEVICE_IDENTIFIER; \ dev["name"]=String(ROLE_NAME) + " (" + String(DEVICE_ID) + ")"; \ dev["mdl"] =String(DEV_MODEL); \ dev["mf"]="ZH"; \ dev["sw"]= String(ZH_PROG_VERSION)+ "," + String(__DATE__) + " " + String(__TIME__); // gateway status config - sent by heartbeat() periodically to HA bool mqtt_publish_gw_status_config() { if (!mqtt_connected) return false; // no mqtt - leaving if (mqtt_publish_gw_status_config_sent) return true; // config published already - no need to repeat, if deleted from HA restart GW bool publish_status = false; //changes locally in each config publish bool total_publish_status = true; //changes globally in each config publish when any fails // mac char mac_conf_topic[80]; snprintf(mac_conf_topic,sizeof(mac_conf_topic),"homeassistant/sensor/%s/mac/config",HOSTNAME); if (debug_mode) Serial.println("mac_conf_topic="+String(mac_conf_topic)); char mac_name[30]; snprintf(mac_name,sizeof(mac_name),"%s_mac",HOSTNAME); if (debug_mode) Serial.println("mac_name="+String(mac_name)); // ip char ip_conf_topic[80]; snprintf(ip_conf_topic,sizeof(ip_conf_topic),"homeassistant/sensor/%s/ip/config",HOSTNAME); if (debug_mode) Serial.println("ip_conf_topic="+String(ip_conf_topic)); char ip_name[30]; snprintf(ip_name,sizeof(ip_name),"%s_ip",HOSTNAME); if (debug_mode) Serial.println("ip_name="+String(ip_name)); // status char status_conf_topic[80]; snprintf(status_conf_topic,sizeof(status_conf_topic),"homeassistant/sensor/%s/status/config",HOSTNAME); if (debug_mode) Serial.println("status_conf_topic="+String(status_conf_topic)); char status_name[30]; snprintf(status_name,sizeof(status_name),"%s_mystatus",HOSTNAME); if (debug_mode) Serial.println("status_name="+String(status_name)); // uptime char uptime_conf_topic[80]; snprintf(uptime_conf_topic,sizeof(uptime_conf_topic),"homeassistant/sensor/%s/uptime/config",HOSTNAME); if (debug_mode) Serial.println("uptime_conf_topic="+String(uptime_conf_topic)); char uptime_name[30]; snprintf(uptime_name,sizeof(uptime_name),"%s_uptime",HOSTNAME); if (debug_mode) Serial.println("uptime_name="+String(uptime_name)); // version char version_conf_topic[80]; snprintf(version_conf_topic,sizeof(version_conf_topic),"homeassistant/sensor/%s/version/config",HOSTNAME); if (debug_mode) Serial.println("version_conf_topic="+String(version_conf_topic)); char version_name[30]; snprintf(version_name,sizeof(version_name),"%s_version",HOSTNAME); if (debug_mode) Serial.println("version_name="+String(version_name)); // rssi of gateway<->AP char rssi_conf_topic[80]; snprintf(rssi_conf_topic,sizeof(rssi_conf_topic),"homeassistant/sensor/%s/rssi/config",HOSTNAME); if (debug_mode) Serial.println("rssi_conf_topic="+String(rssi_conf_topic)); char rssi_name[30]; snprintf(rssi_name,sizeof(rssi_name),"%s_rssi",HOSTNAME); if (debug_mode) Serial.println("rssi_name="+String(rssi_name)); // common topic char status_state_topic[80]; snprintf(status_state_topic,sizeof(status_state_topic),"%s/%s/sensor/state",MQTT_TOP_LEVEL_TOPIC,HOSTNAME); if (debug_mode) Serial.println("status_state_topic="+String(status_state_topic)); StaticJsonDocument config; JsonObject dev; int size_c; char config_json[JSON_CONFIG_SIZE]; // gw mac config config.clear(); config["name"] = "mac"; config["stat_t"] = status_state_topic; config["val_tpl"] = "{{value_json.mac}}"; config["uniq_id"] = mac_name; config["obj_id"] = mac_name; config["frc_upd"] = "true"; config["entity_category"] = "diagnostic"; // config["exp_aft"] = HEARTBEAT_INTERVAL_S * 3; CREATE_GW_MQTT_DEVICE size_c = serializeJson(config, config_json); if (!mqttc.publish(mac_conf_topic,(uint8_t*)config_json,strlen(config_json), true)) { publish_status = false; total_publish_status = false; Serial.printf("[%s]: PUBLISH FAILED for %s\n",__func__,mac_conf_topic); } else { publish_status = true; if (debug_mode) {Serial.printf("[%s]: PUBLISH SUCCESSFULL for %s\n",__func__,mac_conf_topic);} } if (debug_mode) { Serial.println("\n============ DEBUG CONFIG MAC ============"); Serial.println("Size of mac config="+String(size_c)+" bytes"); Serial.println("Serialised config_json:"); Serial.println(config_json); Serial.println("serializeJsonPretty"); serializeJsonPretty(config, Serial); if (publish_status) { Serial.println("\n MAC CONFIG OK"); } else { Serial.println("\n PRETTYONTIME CONFIG UNSUCCESSFULL"); } Serial.println("============ DEBUG CONFIG MAC END ========\n"); } // gw ip config config.clear(); config["name"] = "ip"; config["stat_t"] = status_state_topic; config["val_tpl"] = "{{value_json.ip}}"; config["uniq_id"] = ip_name; config["obj_id"] = ip_name; config["frc_upd"] = "true"; config["entity_category"] = "diagnostic"; // config["exp_aft"] = HEARTBEAT_INTERVAL_S * 3; CREATE_GW_MQTT_DEVICE size_c = serializeJson(config, config_json); if (!mqttc.publish(ip_conf_topic,(uint8_t*)config_json,strlen(config_json), true)) { publish_status = false; total_publish_status = false; Serial.printf("[%s]: PUBLISH FAILED for %s\n",__func__,ip_conf_topic); } else { publish_status = true; if (debug_mode) {Serial.printf("[%s]: PUBLISH SUCCESSFULL for %s\n",__func__,ip_conf_topic);} } if (debug_mode) { Serial.println("\n============ DEBUG CONFIG IP ============"); Serial.println("Size of ip config="+String(size_c)+" bytes"); Serial.println("Serialised config_json:"); Serial.println(config_json); Serial.println("serializeJsonPretty"); serializeJsonPretty(config, Serial); if (publish_status) { Serial.println("\n IP CONFIG OK"); } else { Serial.println("\n PRETTYONTIME CONFIG UNSUCCESSFULL"); } Serial.println("============ DEBUG CONFIG IP END ========\n"); } // status config config.clear(); config["name"] = "my_status"; config["stat_t"] = status_state_topic; config["qos"] = 2; config["uniq_id"] = status_name; config["obj_id"] = status_name; config["val_tpl"] = "{{value_json.status}}"; config["frc_upd"] = "true"; config["entity_category"] = "diagnostic"; // config["exp_aft"] = HEARTBEAT_INTERVAL_S * 3; CREATE_GW_MQTT_DEVICE size_c = serializeJson(config, config_json); if (!mqttc.publish(status_conf_topic,(uint8_t*)config_json,strlen(config_json), true)) { publish_status = false; total_publish_status = false; Serial.printf("[%s]: PUBLISH FAILED for %s\n",__func__,status_conf_topic); } else { publish_status = true; if (debug_mode) {Serial.printf("[%s]: PUBLISH SUCCESSFULL for %s\n",__func__,status_conf_topic);} } if (debug_mode) { Serial.println("\n============ DEBUG [STATUS] CONFIG: ============"); Serial.println("Size of config_json="+String(size_c)+" bytes"); Serial.println("Serialised config_json:"); Serial.println(config_json); Serial.println("serializeJsonPretty(config, Serial)"); serializeJsonPretty(config, Serial); if (publish_status) { Serial.println("\n CONFIG published OK"); } else { Serial.println("\n CONFIG UNSUCCESSFULL"); } Serial.println("============ DEBUG [STATUS] CONFIG END ========\n"); } // uptime config config.clear(); config["name"] = "uptime"; config["stat_t"] = status_state_topic; config["qos"] = 2; config["retain"] = "true"; config["uniq_id"] = uptime_name; config["obj_id"] = uptime_name; config["val_tpl"] = "{{value_json.uptime}}"; config["frc_upd"] = "true"; config["entity_category"] = "diagnostic"; // config["exp_aft"] = HEARTBEAT_INTERVAL_S * 3; CREATE_GW_MQTT_DEVICE size_c = serializeJson(config, config_json); if (!mqttc.publish(uptime_conf_topic,(uint8_t*)config_json,strlen(config_json), true)) { publish_status = false; total_publish_status = false; Serial.printf("[%s]: PUBLISH FAILED for %s\n",__func__,uptime_conf_topic); } else { publish_status = true; if (debug_mode) {Serial.printf("[%s]: PUBLISH SUCCESSFULL for %s\n",__func__,uptime_conf_topic);} } if (debug_mode) { Serial.println("\n============ DEBUG [UPTIME] CONFIG: ============"); Serial.println("Size of config_json="+String(size_c)+" bytes"); Serial.println("Serialised config_json:"); Serial.println(config_json); Serial.println("serializeJsonPretty(config, Serial)"); serializeJsonPretty(config, Serial); if (publish_status) { Serial.println("\n CONFIG published OK"); } else { Serial.println("\n CONFIG UNSUCCESSFULL"); } Serial.println("============ DEBUG [UPTIME] CONFIG END ========\n"); } // version config config.clear(); config["name"] = "version"; config["stat_t"] = status_state_topic; config["qos"] = 2; config["retain"] = "true"; config["uniq_id"] = version_name; config["obj_id"] = version_name; config["val_tpl"] = "{{value_json.version}}"; config["frc_upd"] = "true"; config["entity_category"] = "diagnostic"; // config["exp_aft"] = HEARTBEAT_INTERVAL_S * 3; CREATE_GW_MQTT_DEVICE size_c = serializeJson(config, config_json); if (!mqttc.publish(version_conf_topic,(uint8_t*)config_json,strlen(config_json), true)) { publish_status = false; total_publish_status = false; Serial.printf("[%s]: PUBLISH FAILED for %s\n",__func__,version_conf_topic); } else { publish_status = true; if (debug_mode) {Serial.printf("[%s]: PUBLISH SUCCESSFULL for %s\n",__func__,version_conf_topic);} } if (debug_mode) { Serial.println("\n============ DEBUG [ZH_PROG_VERSION] CONFIG: ============"); Serial.println("Size of config_json="+String(size_c)+" bytes"); Serial.println("Serialised config_json:"); Serial.println(config_json); Serial.println("serializeJsonPretty(config, Serial)"); serializeJsonPretty(config, Serial); if (publish_status) { Serial.println("\n CONFIG published OK"); } else { Serial.println("\n CONFIG UNSUCCESSFULL"); } Serial.println("============ DEBUG [ZH_PROG_VERSION] CONFIG END ========\n"); } // rssi config config.clear(); config["name"] = "rssi"; config["dev_cla"] = "signal_strength"; config["stat_cla"] = "measurement"; config["stat_t"] = status_state_topic; config["unit_of_meas"] = "dBm"; config["val_tpl"] = "{{value_json.rssi}}"; config["uniq_id"] = rssi_name; config["obj_id"] = rssi_name; config["frc_upd"] = "true"; config["entity_category"] = "diagnostic"; // config["exp_aft"] = HEARTBEAT_INTERVAL_S * 3; CREATE_GW_MQTT_DEVICE size_c = serializeJson(config, config_json); if (!mqttc.publish(rssi_conf_topic,(uint8_t*)config_json,strlen(config_json), true)) { publish_status = false; total_publish_status = false; Serial.printf("[%s]: PUBLISH FAILED for %s\n",__func__,rssi_conf_topic); } else { publish_status = true; if (debug_mode) {Serial.printf("[%s]: PUBLISH SUCCESSFULL for %s\n",__func__,rssi_conf_topic);} } if (debug_mode) { Serial.println("\n============ DEBUG [RSSI] CONFIG ============"); Serial.println("Size of config="+String(size_c)+" bytes"); Serial.println("Serialised config_json:"); Serial.println(config_json); Serial.println("serializeJsonPretty"); serializeJsonPretty(config, Serial); if (publish_status) { Serial.println("\n CONFIG published OK"); } else { Serial.println("\n CONFIG UNSUCCESSFULL"); } Serial.println("============ DEBUG [RSSI] CONFIG END ========\n"); } mqtt_publish_gw_status_config_sent = total_publish_status; return total_publish_status; } // gateway status value bool mqtt_publish_gw_status_values(const char* status) { // debug_mode = true; if (!mqtt_connected) return false; bool publish_status = false; char status_state_topic[80]; snprintf(status_state_topic,sizeof(status_state_topic),"%s/%s/sensor/state",MQTT_TOP_LEVEL_TOPIC,HOSTNAME); if (debug_mode) Serial.println("status_state_topic="+String(status_state_topic)); // decoupled inside publish_config() if (!mqtt_publish_gw_status_config()){ Serial.println("\n GW STATUS CONFIG NOT published"); return false; } StaticJsonDocument payload; payload["status"] = status; payload["rssi"] = WiFi.RSSI(); payload["mac"] = WiFi.macAddress(); IPAddress ip = WiFi.localIP(); char ip_char[30]; snprintf(ip_char,sizeof(ip_char),"%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]); payload["ip"] = ip_char; // Serial.print("IP=");Serial.println(WiFi.localIP()); // Serial.print("ip_char=");Serial.println(ip_char); char ret_clk[60]; uptime(ret_clk); payload["uptime"] = ret_clk; payload["version"] = ZH_PROG_VERSION; char payload_json[JSON_PAYLOAD_SIZE]; int size_pl = serializeJson(payload, payload_json); #if (RETAIN_GATEWAY_DATA == 1) if (!mqttc.publish(status_state_topic,(uint8_t*)payload_json,strlen(payload_json), true)) #else if (!mqttc.publish(status_state_topic,(uint8_t*)payload_json,strlen(payload_json), false)) #endif { publish_status = false; Serial.printf("[%s]: PUBLISH FAILED for %s\n",__func__,status_state_topic); } else { publish_status = true; if (debug_mode) {Serial.printf("[%s]: PUBLISH SUCCESSFULL for %s\n",__func__,status_state_topic);} } if (debug_mode) { Serial.println("\n============ DEBUG [STATUS] PAYLOAD ============"); Serial.println("Size of payload="+String(size_pl)+" bytes"); Serial.println("Serialised payload_json:"); Serial.println(payload_json); Serial.println("serializeJsonPretty"); serializeJsonPretty(payload, Serial); if (publish_status) { Serial.println("\n STATUS VALUES published OK"); } else { Serial.println("\n STATUS VALUES NOT published"); } Serial.println("============ DEBUG [STATUS] PAYLOAD END ========\n"); } #if (USE_WEB_SERIAL == 1) if (print2web) WebSerial.println(payload_json); #endif return publish_status; } // stop/start updating HA with sensors data - switch on HA bool mqtt_publish_switch_publish_config() { if (!mqtt_connected) return false; // no mqtt - leaving if (mqtt_publish_switch_publish_config_sent) return true; // config published already - no need to repeat, if deleted from HA restart GW bool publish_status = false; //changes locally in each config publish bool total_publish_status = true; //changes globally in each config publish when any fails char publish_conf_topic[80]; snprintf(publish_conf_topic,sizeof(publish_conf_topic),"homeassistant/switch/%s/publish/config",HOSTNAME); if (debug_mode) Serial.println("publish_conf_topic="+String(publish_conf_topic)); char publish_name[30]; snprintf(publish_name,sizeof(publish_name),"%s_publish",HOSTNAME); if (debug_mode) Serial.println("publish_name="+String(publish_name)); char publish_state_topic[80]; snprintf(publish_state_topic,sizeof(publish_state_topic),"%s/%s/switch/publish",MQTT_TOP_LEVEL_TOPIC,HOSTNAME); if (debug_mode) Serial.println("publish_state_topic="+String(publish_state_topic)); char publish_cmd_topic[80]; snprintf(publish_cmd_topic,sizeof(publish_cmd_topic),"%s/%s/cmd/publish",MQTT_TOP_LEVEL_TOPIC,HOSTNAME); if (debug_mode) Serial.println("restart_cmd_topic="+String(publish_cmd_topic)); StaticJsonDocument config; JsonObject dev; int size_c; char config_json[JSON_CONFIG_SIZE]; /* MQTT sensor or binary_sensor configuration with an entity_category explicitly set to config will fail to set up. Maintainers should set the entity_category ttribute to diagnostic or omit the config attribute. */ // publish config config.clear(); config["name"] = "publish"; config["stat_t"] = publish_state_topic; config["command_topic"] = publish_cmd_topic; config["payload_on"] = "ON"; config["payload_off"] = "OFF"; // config["optimistic"] = "true"; config["qos"] = "2"; config["retain"] = "true"; config["device_class"] = "switch"; config["uniq_id"] = publish_name; config["obj_id"] = publish_name; config["entity_category"] = "config"; config["val_tpl"] = "{{value_json.publish}}"; // config["exp_aft"] = HEARTBEAT_INTERVAL_S * 3; CREATE_GW_MQTT_DEVICE size_c = serializeJson(config, config_json); if (!mqttc.publish(publish_conf_topic,(uint8_t*)config_json,strlen(config_json), true)) { publish_status = false; total_publish_status = false; Serial.printf("[%s]: PUBLISH FAILED for %s\n",__func__,publish_conf_topic); } else { publish_status = true; if (debug_mode) {Serial.printf("[%s]: PUBLISH SUCCESSFULL for %s\n",__func__,publish_conf_topic);} } if (debug_mode) { Serial.println("\n============ DEBUG [PUBLISH SWITCH] CONFIG ============"); Serial.println("Size of config_json="+String(size_c)+" bytes"); Serial.println("Serialised config_json:"); Serial.println(config_json); Serial.println("serializeJsonPretty(config, Serial)"); serializeJsonPretty(config, Serial); if (publish_status) { Serial.println("\n CONFIG published OK"); } else { Serial.println("\n CONFIG UNSUCCESSFULL"); } Serial.println("============ DEBUG [PUBLISH SWITCH] CONFIG END ========\n"); } mqtt_publish_switch_publish_config_sent = total_publish_status; return total_publish_status; } bool mqtt_publish_switch_publish_values() { if (!mqtt_connected) return false; bool publish_status = false; char publish_state_topic[80]; snprintf(publish_state_topic,sizeof(publish_state_topic),"%s/%s/switch/publish",MQTT_TOP_LEVEL_TOPIC,HOSTNAME); if (debug_mode) Serial.println("publish_state_topic="+String(publish_state_topic)); // managed inside publish_config() if (!mqtt_publish_switch_publish_config()) { Serial.printf("[%s]: PUBLISH CONFIG NOT published, leaving\n",__func__); return false; } StaticJsonDocument payload; if (publish_sensors_to_ha) {payload["publish"] = "ON";} else {payload["publish"] = "OFF";} char payload_json[JSON_PAYLOAD_SIZE]; int size_pl = serializeJson(payload, payload_json); #if (RETAIN_GATEWAY_DATA == 1) if (!mqttc.publish(publish_state_topic,(uint8_t*)payload_json,strlen(payload_json), true)) #else if (!mqttc.publish(publish_state_topic,(uint8_t*)payload_json,strlen(payload_json), false)) #endif { publish_status = false; Serial.printf("[%s]: PUBLISH FAILED for %s\n",__func__,publish_state_topic); } else { publish_status = true; if (debug_mode) {Serial.printf("[%s]: PUBLISH SUCCESSFULL for %s\n",__func__,publish_state_topic);} } if (debug_mode) { Serial.println("\n============ DEBUG: [PUBLISH SWITCH] PAYLOAD ============"); Serial.println("Size of payload="+String(size_pl)+" bytes"); Serial.println("Serialised payload_json:"); Serial.println(payload_json); Serial.println("serializeJsonPretty"); serializeJsonPretty(payload, Serial); if (publish_status) { Serial.println("\n publish VALUES published OK"); } else { Serial.println("\n publish VALUES NOT published"); } Serial.println("============ DEBUG: [PUBLISH SWITCH] END ========\n"); } #if (USE_WEB_SERIAL == 1) if (print2web) WebSerial.println(payload_json); #endif return publish_status; } // periodic update of everything from gateway void heartbeat() { unsigned long function_start = millis(); bool publish_status = true; set_gateway_led_level(1); char pretty_ontime[17]; // "999d 24h 60m 60s" = 16 characters ConvertSectoDay(millis()/1000,pretty_ontime); Serial.printf("[%s]: updating GW status %s after boot...",__func__,pretty_ontime); if (calibration_in_progress) { publish_status = publish_status && mqtt_publish_gw_status_values("calibration"); } else { publish_status = publish_status && mqtt_publish_gw_status_values("online"); } // initial, one time only configs - for universal functions only - for fixed ones it is done in publish_config() if (!mqtt_publish_button_update_config_sent) { bool tmp1 = mqtt_publish_button_config("update"); mqtt_publish_button_update_config_sent = tmp1; publish_status = publish_status && tmp1; } if (!mqtt_publish_button_config_reset_sent) { bool tmp1 = mqtt_publish_button_config("reset"); mqtt_publish_button_config_reset_sent = tmp1; publish_status = publish_status && tmp1; } if (!mqtt_publish_button_config_reset_cmd_queue_sent) { bool tmp1 = mqtt_publish_button_config("reset_cmd_queue"); mqtt_publish_button_config_reset_cmd_queue_sent = tmp1; publish_status = publish_status && tmp1; } if (!mqtt_publish_light_config_sensors_sent) { bool tmp1 = mqtt_publish_light_config("led_sensors",false); mqtt_publish_light_config_sensors_sent = tmp1; publish_status = publish_status && tmp1; } if (!mqtt_publish_light_config_gateway_sent) { bool tmp1 = mqtt_publish_light_config("led_gateway",false); mqtt_publish_light_config_gateway_sent = tmp1; publish_status = publish_status && tmp1; } if (!mqtt_publish_light_config_standby_sent) { bool tmp1 = mqtt_publish_light_config("led_standby",false); mqtt_publish_light_config_standby_sent = tmp1; publish_status = publish_status && tmp1; } if (!mqtt_publish_text_sensor_value_available_sent) { bool tmp1 = mqtt_publish_text_sensor_config("available"); mqtt_publish_text_sensor_value_available_sent = tmp1; publish_status = publish_status && tmp1; } if (!mqtt_publish_switch_publish_config_sent) { bool tmp1 = mqtt_publish_switch_publish_config(); mqtt_publish_switch_publish_config_sent = tmp1; publish_status = publish_status && tmp1; } if (!mqtt_publish_text_sensor_value_queue_size_sent) { bool tmp1 = mqtt_publish_text_sensor_config("cmd_queue_size"); mqtt_publish_text_sensor_value_queue_size_sent = tmp1; publish_status = publish_status && tmp1; } if (!mqtt_publish_text_sensor_value_last_updated_sensor_sent) { bool tmp1 = mqtt_publish_text_sensor_config("last"); mqtt_publish_text_sensor_value_last_updated_sensor_sent = tmp1; publish_status = publish_status && tmp1; } // restart button left with config always so it is possible to restart from HA after deletion // if (!mqtt_publish_button_restart_config_sent) // { bool tmp1 = mqtt_publish_button_config("restart"); mqtt_publish_button_restart_config_sent = tmp1; publish_status = publish_status && tmp1; // } // publish initial value of the switch publish_status = publish_status && mqtt_publish_switch_publish_values(); // publish periodic available publish_status = publish_status && mqtt_publish_text_sensor_value("available","online"); if (!mqtt_published_to_ha) { if (publish_status) { mqtt_published_to_ha = true; write_badbootcount(0); } } #if (USE_BMP280 == 1) char pressure_chr[10]; char tempbmp280_chr[10]; measure_temp_pressure(pressure_chr,tempbmp280_chr); publish_status = publish_status && mqtt_publish_sensor_with_unit_value("airpressure","hPa",pressure_chr); publish_status = publish_status && mqtt_publish_sensor_with_unit_value("bmp280_temperature","°C",tempbmp280_chr); #endif #if (MEASURE_LUX == 1) char lux[7]; get_lux(lux); publish_status = publish_status && mqtt_publish_sensor_with_unit_value("lux","lx",lux); #endif #if (USE_MHZ19_CO2 == 1) if (co2_received) publish_status = publish_status && mqtt_publish_sensor_with_unit_value("co2", "ppm",co2); #endif int queue_count = uxQueueMessagesWaiting(queue); // send queue status if (!publish_sensors_to_ha) { char queue_status[20]; snprintf(queue_status, sizeof(queue_status), "queue: %d/%d",queue_count,MAX_QUEUE_COUNT); publish_status = publish_status && mqtt_publish_text_sensor_value("last",queue_status); } // send warning if (queue_count == MAX_QUEUE_COUNT) { Serial.printf("[%s]: ERROR: queue is full!\n",__func__); publish_status = publish_status && mqtt_publish_text_sensor_value("last","N/A"); } if (publish_status) { Serial.println("SUCCESSFULL"); } else { Serial.println("FAILED"); } u_int8_t queue_commands_count = uxQueueMessagesWaiting(queue_commands); char queue_size_char[4]; // check if correction works it as it was counting time from restart ;-) // reset the queue with commands for senders if timeout expired, count from last command received if (queue_commands_count > 0) { if ((millis() - last_cmd_received) >= (COMMAND_QUEUE_TIMEOUT_S * 1000)) { xQueueReset( queue_commands ); queue_commands_count = uxQueueMessagesWaiting(queue_commands); snprintf(queue_size_char,sizeof(queue_size_char),"%d",queue_commands_count); mqtt_publish_text_sensor_value("cmd_queue_size", queue_size_char); } } snprintf(queue_size_char,sizeof(queue_size_char),"%d",queue_commands_count); mqtt_publish_text_sensor_value("cmd_queue_size", queue_size_char); #ifdef DEBUG Serial.printf("[%s]: Commands for senders in the queue: %d\n",__func__,queue_commands_count); #endif // reset the queue with commands for senders if queue is full if (queue_commands_count >= MAX_QUEUE_COMMANDS_COUNT) { publish_status = publish_status && mqtt_publish_gw_status_values("cmd queue FULL"); Serial.print("MAX_QUEUE_COMMANDS_COUNT reached: ");Serial.println(queue_commands_count); Serial.println("resetting the queue with commands for senders..."); xQueueReset( queue_commands ); queue_commands_count = uxQueueMessagesWaiting(queue_commands); #ifdef DEBUG Serial.print("New commands in the queue: ");Serial.println(queue_commands_count); #endif publish_status = publish_status && mqtt_publish_gw_status_values("online"); } // reset the queue with commands for senders if queue is full END set_gateway_led_level(0); // Serial.printf(" -> it took %ums\n",(millis()-function_start)); } // ================== UNIVERSAL FUNCTIONS ======================== // universal button on HA - config bool mqtt_publish_button_config(const char* button) { if (!mqtt_connected) return false; bool debug_mode = false; bool publish_status = false; //changes locally in each config publish bool total_publish_status = true; //changes globally in each config publish when any fails char button_conf_topic[100]; snprintf(button_conf_topic,sizeof(button_conf_topic),"homeassistant/button/%s/%s/config",HOSTNAME,button); if (debug_mode) Serial.println("button_conf_topic="+String(button_conf_topic)); char button_name[30]; snprintf(button_name,sizeof(button_name),"%s_%s",HOSTNAME,button); if (debug_mode) Serial.println("button_name="+String(button_name)); char button_state_topic[80]; snprintf(button_state_topic,sizeof(button_state_topic),"%s/%s/button/%s",MQTT_TOP_LEVEL_TOPIC,HOSTNAME,button); if (debug_mode) Serial.println("button_state_topic="+String(button_state_topic)); char button_cmd_topic[80]; snprintf(button_cmd_topic,sizeof(button_cmd_topic),"%s/%s/cmd/%s",MQTT_TOP_LEVEL_TOPIC,HOSTNAME,button); if (debug_mode) Serial.println("button_cmd_topic="+String(button_cmd_topic)); char button_val_tpl[30]; snprintf(button_val_tpl,sizeof(button_val_tpl),"{{value_json.%s}}",button); if (debug_mode) Serial.println("button_val_tpl="+String(button_val_tpl)); StaticJsonDocument config; JsonObject dev; int size_c; char config_json[JSON_CONFIG_SIZE]; // button config config.clear(); config["name"] = button; config["command_topic"] = button_cmd_topic; config["payload_press"] = "ON"; config["qos"] = "2"; config["retain"] = "false"; config["uniq_id"] = button_name; config["obj_id"] = button_name; config["val_tpl"] = button_val_tpl; // config["exp_aft"] = HEARTBEAT_INTERVAL_S * 3; CREATE_GW_MQTT_DEVICE size_c = serializeJson(config, config_json); if (!mqttc.publish(button_conf_topic,(uint8_t*)config_json,strlen(config_json), true)) { publish_status = false; total_publish_status = false; Serial.printf("[%s]: PUBLISH FAILED for %s\n",__func__,button_conf_topic); } else { publish_status = true; if (debug_mode) {Serial.printf("[%s]: PUBLISH SUCCESSFULL for %s\n",__func__,button_conf_topic);} } if (debug_mode) { Serial.println("\n============ DEBUG [BUTTON] CONFIG: ============"); Serial.println("Size of config_json="+String(size_c)+" bytes"); Serial.println("Serialised config_json:"); Serial.println(config_json); Serial.println("serializeJsonPretty(config, Serial)"); serializeJsonPretty(config, Serial); if (publish_status) { Serial.println("\n BUTTON CONFIG published OK"); } else { Serial.println("\n BUTTON CONFIG UNSUCCESSFULL"); } Serial.println("============ DEBUG [BUTTON] CONFIG END ========\n"); } return total_publish_status; } // universal standalone text sensor on HA - config bool mqtt_publish_text_sensor_config(const char* text_sensor) { if (!mqtt_connected) return false; bool debug_mode = false; bool publish_status = false; //changes locally in each config publish bool total_publish_status = true; //changes globally in each config publish when any fails char text_sensor_conf_topic[100]; snprintf(text_sensor_conf_topic,sizeof(text_sensor_conf_topic),"homeassistant/sensor/%s/%s/config",HOSTNAME,text_sensor); if (debug_mode) Serial.println("text_sensor_conf_topic="+String(text_sensor_conf_topic)); char text_sensor_name[30]; snprintf(text_sensor_name,sizeof(text_sensor_name),"%s_%s",HOSTNAME,text_sensor); if (debug_mode) Serial.println("text_sensor_name="+String(text_sensor_name)); char text_sensor_state_topic[80]; if (strcmp(text_sensor,"available")== 0) { snprintf(text_sensor_state_topic,sizeof(text_sensor_state_topic),"%s/%s/%s",MQTT_TOP_LEVEL_TOPIC,HOSTNAME,text_sensor); } else { snprintf(text_sensor_state_topic,sizeof(text_sensor_state_topic),"%s/%s/sensor/%s",MQTT_TOP_LEVEL_TOPIC,HOSTNAME,text_sensor); } // snprintf(text_sensor_state_topic,sizeof(text_sensor_state_topic),"%s/%s/sensor/%s",MQTT_TOP_LEVEL_TOPIC,HOSTNAME,text_sensor); if (debug_mode) Serial.println("text_sensor_state_topic="+String(text_sensor_state_topic)); char text_sensor_val_tpl[30]; snprintf(text_sensor_val_tpl,sizeof(text_sensor_val_tpl),"{{value_json.%s}}",text_sensor); if (debug_mode) Serial.println("text_sensor_val_tpl="+String(text_sensor_val_tpl)); StaticJsonDocument config; JsonObject dev; int size_c; char config_json[JSON_CONFIG_SIZE]; // text_sensor config config.clear(); config["name"] = text_sensor; config["qos"] = "2"; config["retain"] = "true"; config["uniq_id"] = text_sensor_name; config["obj_id"] = text_sensor_name; config["val_tpl"] = text_sensor_val_tpl; config["stat_t"] = text_sensor_state_topic; config["frc_upd"] = "true"; if (strcmp(text_sensor,"available")== 0) { config["entity_category"] = "diagnostic"; } // config["exp_aft"] = HEARTBEAT_INTERVAL_S * 3; CREATE_GW_MQTT_DEVICE size_c = serializeJson(config, config_json); if (!mqttc.publish(text_sensor_conf_topic,(uint8_t*)config_json,strlen(config_json), true)) { publish_status = false; total_publish_status = false; Serial.printf("[%s]: PUBLISH FAILED for %s\n",__func__,text_sensor_conf_topic); } else { publish_status = true; if (debug_mode) {Serial.printf("[%s]: PUBLISH SUCCESSFULL for %s\n",__func__,text_sensor_conf_topic);} } if (debug_mode) { Serial.println("\n============ DEBUG [text_sensor] CONFIG: ============"); Serial.println("Size of config_json="+String(size_c)+" bytes"); Serial.println("Serialised config_json:"); Serial.println(config_json); Serial.println("serializeJsonPretty(config, Serial)"); serializeJsonPretty(config, Serial); if (publish_status) { Serial.println("\n text_sensor CONFIG published OK"); } else { Serial.println("\n text_sensor CONFIG UNSUCCESSFULL"); } Serial.println("============ DEBUG [text_sensor] CONFIG END ========\n"); } return total_publish_status; } // universal standalone text sensor on HA - publish value bool mqtt_publish_text_sensor_value(const char* text_sensor, const char* text_sensor_value) { if (!mqtt_connected) return false; bool debug_mode = false; bool publish_status = false; char text_sensor_state_topic[80]; if (strcmp(text_sensor,"available")== 0) { snprintf(text_sensor_state_topic,sizeof(text_sensor_state_topic),"%s/%s/%s",MQTT_TOP_LEVEL_TOPIC,HOSTNAME,text_sensor); } else { snprintf(text_sensor_state_topic,sizeof(text_sensor_state_topic),"%s/%s/sensor/%s",MQTT_TOP_LEVEL_TOPIC,HOSTNAME,text_sensor); } // snprintf(text_sensor_state_topic,sizeof(text_sensor_state_topic),"%s/%s/sensor/%s",MQTT_TOP_LEVEL_TOPIC,HOSTNAME,text_sensor); if (debug_mode) Serial.println("text_sensor_state_topic="+String(text_sensor_state_topic)); // decoupled from here // if (!mqtt_publish_text_sensor_config(text_sensor)) // { // Serial.printf("[%s]: PUBLISH CONFIG NOT published, leaving\n",__func__); // return false; // } StaticJsonDocument payload; payload[text_sensor] = text_sensor_value; char payload_json[JSON_PAYLOAD_SIZE]; int size_pl = serializeJson(payload, payload_json); #if (RETAIN_GATEWAY_DATA == 1) if (!mqttc.publish(text_sensor_state_topic,(uint8_t*)payload_json,strlen(payload_json), true)) #else if (!mqttc.publish(text_sensor_state_topic,(uint8_t*)payload_json,strlen(payload_json), false)) #endif { publish_status = false; Serial.printf("[%s]: PUBLISH FAILED for %s\n",__func__,text_sensor_state_topic); } else { publish_status = true; if (debug_mode) {Serial.printf("[%s]: PUBLISH SUCCESSFULL for %s\n",__func__,text_sensor_state_topic);} } if (debug_mode) { Serial.println("\n============ DEBUG [text_sensor_value] PAYLOAD ============"); Serial.println("Size of payload="+String(size_pl)+" bytes"); Serial.println("Serialised payload_json:"); Serial.println(payload_json); Serial.println("serializeJsonPretty"); serializeJsonPretty(payload, Serial); if (publish_status) { Serial.println("\n text_sensor_value VALUES published OK"); } else { Serial.println("\n text_sensor_value VALUES NOT published"); } Serial.println("============ DEBUG [text_sensor_value] PAYLOAD END ========\n"); } return publish_status; } // universal standalone sensor with unit on HA - config bool mqtt_publish_sensor_with_unit_config(const char* sensor_with_unit, const char* sensor_unit) { if (!mqtt_connected) return false; bool debug_mode = false; bool publish_status = false; //changes locally in each config publish bool total_publish_status = true; //changes globally in each config publish when any fails char sensor_with_unit_conf_topic[150]; snprintf(sensor_with_unit_conf_topic,sizeof(sensor_with_unit_conf_topic),"homeassistant/sensor/%s/%s/config",HOSTNAME,sensor_with_unit); if (debug_mode) Serial.println("sensor_with_unit_conf_topic="+String(sensor_with_unit_conf_topic)); char sensor_with_unit_name[100]; snprintf(sensor_with_unit_name,sizeof(sensor_with_unit_name),"%s_%s",HOSTNAME,sensor_with_unit); if (debug_mode) Serial.println("sensor_with_unit_name="+String(sensor_with_unit_name)); char sensor_with_unit_state_topic[100]; snprintf(sensor_with_unit_state_topic,sizeof(sensor_with_unit_state_topic),"%s/%s/sensor/%s",MQTT_TOP_LEVEL_TOPIC,HOSTNAME,sensor_with_unit); if (debug_mode) Serial.println("sensor_with_unit_state_topic="+String(sensor_with_unit_state_topic)); char sensor_with_unit_val_tpl[100]; snprintf(sensor_with_unit_val_tpl,sizeof(sensor_with_unit_val_tpl),"{{value_json.%s}}",sensor_with_unit); if (debug_mode) Serial.println("sensor_with_unit_val_tpl="+String(sensor_with_unit_val_tpl)); StaticJsonDocument config; JsonObject dev; int size_c; char config_json[JSON_CONFIG_SIZE]; // sensor_with_unit config config.clear(); config["name"] = sensor_with_unit; config["qos"] = "2"; config["retain"] = "true"; config["uniq_id"] = sensor_with_unit_name; config["obj_id"] = sensor_with_unit_name; config["val_tpl"] = sensor_with_unit_val_tpl; config["stat_t"] = sensor_with_unit_state_topic; config["frc_upd"] = "true"; config["unit_of_meas"] = sensor_unit; // config["exp_aft"] = HEARTBEAT_INTERVAL_S * 3; CREATE_GW_MQTT_DEVICE size_c = serializeJson(config, config_json); if (!mqttc.publish(sensor_with_unit_conf_topic,(uint8_t*)config_json,strlen(config_json), true)) { publish_status = false; total_publish_status = false; Serial.printf("[%s]: PUBLISH FAILED for %s\n",__func__,sensor_with_unit_conf_topic); } else { publish_status = true; if (debug_mode) {Serial.printf("[%s]: PUBLISH SUCCESSFULL for %s\n",__func__,sensor_with_unit_conf_topic);} } if (debug_mode) { Serial.println("\n============ DEBUG [sensor_with_unit] CONFIG: ============"); Serial.println("Size of config_json="+String(size_c)+" bytes"); Serial.println("Serialised config_json:"); Serial.println(config_json); Serial.println("serializeJsonPretty(config, Serial)"); serializeJsonPretty(config, Serial); if (publish_status) { Serial.println("\n sensor_with_unit CONFIG published OK"); } else { Serial.println("\n sensor_with_unit CONFIG UNSUCCESSFULL"); } Serial.println("============ DEBUG [sensor_with_unit] CONFIG END ========\n"); } return total_publish_status; } // universal standalone sensor with unit on HA - publish value - value is float - not string! bool mqtt_publish_sensor_with_unit_value(const char* sensor_with_unit, const char* sensor_unit, const char* sensor_with_unit_value) { if (!mqtt_connected) return false; bool debug_mode = false; bool publish_status = false; char sensor_with_unit_state_topic[80]; snprintf(sensor_with_unit_state_topic,sizeof(sensor_with_unit_state_topic),"%s/%s/sensor/%s",MQTT_TOP_LEVEL_TOPIC,HOSTNAME,sensor_with_unit); if (debug_mode) Serial.println("sensor_with_unit_state_topic="+String(sensor_with_unit_state_topic)); // decoupled from here // if (!mqtt_publish_sensor_with_unit_config(sensor_with_unit, sensor_unit)) // { // Serial.printf("[%s]: PUBLISH CONFIG NOT published, leaving\n",__func__); // return false; // } StaticJsonDocument payload; payload[sensor_with_unit] = sensor_with_unit_value; char payload_json[JSON_PAYLOAD_SIZE]; int size_pl = serializeJson(payload, payload_json); #if (RETAIN_GATEWAY_DATA == 1) if (!mqttc.publish(sensor_with_unit_state_topic,(uint8_t*)payload_json,strlen(payload_json), true)) #else if (!mqttc.publish(sensor_with_unit_state_topic,(uint8_t*)payload_json,strlen(payload_json), false)) #endif { publish_status = false; Serial.printf("[%s]: PUBLISH FAILED for %s\n",__func__,sensor_with_unit_state_topic); } else { publish_status = true; if (debug_mode) {Serial.printf("[%s]: PUBLISH SUCCESSFULL for %s\n",__func__,sensor_with_unit_state_topic);} } if (debug_mode) { Serial.println("\n============ DEBUG [sensor_with_unit_value] PAYLOAD ============"); Serial.println("Size of payload="+String(size_pl)+" bytes"); Serial.println("Serialised payload_json:"); Serial.println(payload_json); Serial.println("serializeJsonPretty"); serializeJsonPretty(payload, Serial); if (publish_status) { Serial.println("\n sensor_with_unit_value VALUES published OK"); } else { Serial.println("\n sensor_with_unit_value VALUES NOT published"); } Serial.println("============ DEBUG [sensor_with_unit_value] PAYLOAD END ========\n"); } return publish_status; } // universal light on HA - config bool mqtt_publish_light_config(const char* light, bool optimistic) { if (!mqtt_connected) return false; bool debug_mode = false; bool publish_status = false; //changes locally in each config publish bool total_publish_status = true; //changes globally in each config publish when any fails char light_conf_topic[100]; snprintf(light_conf_topic,sizeof(light_conf_topic),"homeassistant/light/%s/%s/config",HOSTNAME,light); if (debug_mode) Serial.println("light_conf_topic="+String(light_conf_topic)); char light_name[60]; snprintf(light_name,sizeof(light_name),"%s_%s",HOSTNAME,light); if (debug_mode) Serial.println("light_name="+String(light_name)); char light_state_topic[80]; snprintf(light_state_topic,sizeof(light_state_topic),"%s/%s/light/%s/state",MQTT_TOP_LEVEL_TOPIC,HOSTNAME,light); if (debug_mode) Serial.println("light_state_topic="+String(light_state_topic)); char light_cmd_topic[80]; snprintf(light_cmd_topic,sizeof(light_cmd_topic),"%s/%s/light/%s/cmd/switch",MQTT_TOP_LEVEL_TOPIC,HOSTNAME,light); if (debug_mode) Serial.println("light_cmd_topic="+String(light_cmd_topic)); char light_val_tpl[60]; snprintf(light_val_tpl,sizeof(light_val_tpl),"{{value_json.light}}"); if (debug_mode) Serial.println("light_val_tpl="+String(light_val_tpl)); char brightness_cmd_topic[80]; snprintf(brightness_cmd_topic,sizeof(brightness_cmd_topic),"%s/%s/light/%s/cmd/brightness",MQTT_TOP_LEVEL_TOPIC,HOSTNAME,light); if (debug_mode) Serial.println("brightness_cmd_topic="+String(brightness_cmd_topic)); char brightness_val_tpl[60]; snprintf(brightness_val_tpl,sizeof(brightness_val_tpl),"{{value_json.brightness}}"); if (debug_mode) Serial.println("brightness_val_tpl="+String(brightness_val_tpl)); StaticJsonDocument config; JsonObject dev; int size_c; char config_json[JSON_CONFIG_SIZE]; config.clear(); config["name"]=light; config["dev_cla"] = "light"; config["state_topic"]=light_state_topic; config["command_topic"]=light_cmd_topic; config["state_value_template"]="{{value_json.state}}"; config["brightness_state_topic"]=light_state_topic; config["brightness_command_topic"]=brightness_cmd_topic; config["brightness_value_template"]="{{value_json.brightness}}"; config["payload_on"] = "ON"; config["payload_off"] = "OFF"; if (optimistic) config["optimistic"] = "true"; else config["optimistic"] = "false";; config["qos"] = "2"; config["retain"] = "true"; config["uniq_id"]=light_name; config["obj_id"]=light_name; config["frc_upd"] = "true"; // config["exp_aft"] = HEARTBEAT_INTERVAL_S * 3; CREATE_GW_MQTT_DEVICE size_c = serializeJson(config, config_json); if (!mqttc.publish(light_conf_topic,(uint8_t*)config_json,strlen(config_json), true)) { publish_status = false; total_publish_status = false; Serial.printf("[%s]: PUBLISH FAILED for %s\n",__func__,light_conf_topic); } else { publish_status = true; if (debug_mode) {Serial.printf("[%s]: PUBLISH SUCCESSFULL for %s\n",__func__,light_conf_topic);} } if (debug_mode) { Serial.println("\n============ DEBUG [light] CONFIG: ============"); Serial.println("Size of config_json="+String(size_c)+" bytes"); Serial.println("Serialised config_json:"); Serial.println(config_json); Serial.println("serializeJsonPretty(config, Serial)"); serializeJsonPretty(config, Serial); if (publish_status) { Serial.println("\n light CONFIG published OK"); } else { Serial.println("\n light CONFIG UNSUCCESSFULL"); } Serial.println("============ DEBUG [light] CONFIG END ========\n"); } return total_publish_status; } // universal light on HA - values bool mqtt_publish_light_values(const char* light, bool power, u_int8_t brightness) { if (!mqtt_connected) return false; bool debug_mode = false; bool publish_status = false; char light_state_topic[80]; snprintf(light_state_topic,sizeof(light_state_topic),"%s/%s/light/%s/state",MQTT_TOP_LEVEL_TOPIC,HOSTNAME,light); if (debug_mode) Serial.println("light_state_topic="+String(light_state_topic)); // decoupled from here to avoid repetitions // if (!mqtt_publish_light_config(light, false)) // { // Serial.printf("[%s]: PUBLISH CONFIG NOT published, leaving\n",__func__); // return false; // } StaticJsonDocument payload; if (power) payload["state"] = "ON"; else payload["state"] = "OFF"; payload["brightness"] = brightness; char payload_json[JSON_PAYLOAD_SIZE]; int size_pl = serializeJson(payload, payload_json); #if (RETAIN_GATEWAY_DATA == 1) if (!mqttc.publish(light_state_topic,(uint8_t*)payload_json,strlen(payload_json), true)) #else if (!mqttc.publish(light_state_topic,(uint8_t*)payload_json,strlen(payload_json), false)) #endif { publish_status = false; Serial.printf("[%s]: PUBLISH FAILED for %s\n",__func__,light_state_topic); } else { publish_status = true; if (debug_mode) {Serial.printf("[%s]: PUBLISH SUCCESSFULL for %s\n",__func__,light_state_topic);} } if (debug_mode) { Serial.println("\n============ DEBUG [mqtt_publish_light_values] PAYLOAD ============"); Serial.println("Size of payload="+String(size_pl)+" bytes"); Serial.println("Serialised payload_json:"); Serial.println(payload_json); Serial.println("serializeJsonPretty"); serializeJsonPretty(payload, Serial); if (publish_status) { Serial.println("\n mqtt_publish_light_values VALUES published OK"); } else { Serial.println("\n mqtt_publish_light_values VALUES NOT published"); } Serial.println("============ DEBUG [mqtt_publish_light_values] PAYLOAD END ========\n"); } return publish_status; } // ================== UNIVERSAL FUNCTIONS ======================== END