Compare commits

..

No commits in common. "main" and "v1.42" have entirely different histories.
main ... v1.42

4 changed files with 235 additions and 244 deletions

View File

@ -10,7 +10,7 @@ function createXmlHttpObject() {
function load() {
if (xmlHttp.readyState == 0 || xmlHttp.readyState == 4) {
xmlHttp.open('GET', '/config', true);
xmlHttp.open('PUT', '/config.json', true);
xmlHttp.send(null);
xmlHttp.onload = function () {
jsonResponse = JSON.parse(xmlHttp.responseText);
@ -52,14 +52,14 @@ function sendRequest(submit, server) {
function saveSetting(submit) {
server = "/setting?ssid=" + getValue('ssid') + "&password=" + encodeURIComponent(getValue('password'))
+ "&mqttHostName=" + getValue('mqttHostName') + "&mqttHostPort=" + getValue('mqttHostPort')
+ "&mqttUserLogin=" + getValue('mqttUserLogin') + "&mqttUserPassword=" + encodeURIComponent(getValue('mqttUserPassword'))
+ "&topicPrefix=" + getValue('topicPrefix')
+ "&deviceName=" + getValue('deviceName')
+ "&espnowNetName=" + getValue('espnowNetName')
+ "&workMode=" + getSelectValue('workModeSelect')
+ "&ntpHostName=" + getValue('ntpHostName')
+ "&gmtOffset=" + getValue('gmtOffset');
+ "&host=" + getValue('mqttHostName') + "&port=" + getValue('mqttHostPort')
+ "&login=" + getValue('mqttUserLogin') + "&pass=" + encodeURIComponent(getValue('mqttUserPassword'))
+ "&prefix=" + getValue('topicPrefix')
+ "&name=" + getValue('deviceName')
+ "&net=" + getValue('espnowNetName')
+ "&mode=" + getSelectValue('workModeSelect')
+ "&ntp=" + getValue('ntpHostName')
+ "&zone=" + getValue('gmtOffset');
sendRequest(submit, server);
alert("Please restart device for changes apply.");
}

View File

@ -2,7 +2,6 @@
platform = espressif8266
board = esp12e
framework = arduino
build_flags = -D PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK305
board_build.filesystem = littlefs
lib_deps =
https://github.com/aZholtikov/ZHNetwork
@ -17,7 +16,6 @@ lib_deps =
platform = espressif8266
board = esp12e
framework = arduino
build_flags = -D PIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK305
board_build.filesystem = littlefs
upload_port = 192.168.4.1
upload_protocol = espota

View File

@ -4,7 +4,6 @@
#include "Ethernet.h" // https://github.com/arduino-libraries/Ethernet
#include "PubSubClient.h"
#include "LittleFS.h"
#include "EEPROM.h"
#include "Ticker.h"
#include "NTPClient.h"
#include "ZHNetwork.h"
@ -16,6 +15,13 @@
#include "ESP32SSDP.h"
#endif
typedef enum : uint8_t
{
ESP_NOW,
ESP_NOW_WIFI,
ESP_NOW_LAN
} work_mode_t;
void onEspnowMessage(const char *data, const uint8_t *sender);
void onMqttMessage(char *topic, byte *payload, unsigned int length);
@ -36,37 +42,31 @@ void checkMqttAvailability(void);
void mqttPublish(const char *topic, const char *payload, bool retained);
typedef enum : uint8_t
{
ESP_NOW,
ESP_NOW_WIFI,
ESP_NOW_LAN
} work_mode_t;
const String firmware{"1.42"};
String espnowNetName{"DEFAULT"};
uint8_t workMode{ESP_NOW};
struct deviceConfig
{
#if defined(ESP8266)
String deviceName = "ESP-NOW gateway " + String(ESP.getChipId(), HEX);
#endif
#if defined(ESP32)
String deviceName = "ESP-NOW gateway " + String(ESP.getEfuseMac(), HEX);
#endif
String espnowNetName{"DEFAULT"};
uint8_t workMode{ESP_NOW};
String ssid{"SSID"};
String password{"PASSWORD"};
String mqttHostName{"MQTT"};
uint16_t mqttHostPort{1883};
String mqttUserLogin{""};
String mqttUserPassword{""};
String topicPrefix{"homeassistant"};
const char *mqttUserID{"ESP32"};
String ntpHostName{"NTP"};
uint16_t gmtOffset{10800};
} config;
const String firmware{"1.6"};
const char *mqttUserID{"ESP"};
uint8_t w5500Mac[6] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; // Change it if necessary.
@ -82,8 +82,8 @@ PubSubClient mqttWifiClient(wifiClient);
WiFiUDP udpWiFiClient;
EthernetUDP udpEthClient;
NTPClient ntpWiFiClient(udpWiFiClient, config.ntpHostName.c_str(), config.gmtOffset);
NTPClient ntpEthClient(udpEthClient, config.ntpHostName.c_str(), config.gmtOffset);
NTPClient ntpWiFiClient(udpWiFiClient, ntpHostName.c_str(), gmtOffset);
NTPClient ntpEthClient(udpEthClient, ntpHostName.c_str(), gmtOffset);
Ticker mqttAvailabilityCheckTimer;
bool mqttAvailabilityCheckTimerSemaphore{true};
@ -109,13 +109,13 @@ void setup()
loadConfig();
if (config.workMode == ESP_NOW_LAN)
if (workMode == ESP_NOW_LAN)
{
Ethernet.init(5);
Ethernet.begin(w5500Mac);
}
if (config.workMode == ESP_NOW_WIFI)
if (workMode == ESP_NOW_WIFI)
{
#if defined(ESP8266)
WiFi.setSleepMode(WIFI_NONE_SLEEP);
@ -128,9 +128,9 @@ void setup()
WiFi.setAutoReconnect(true);
}
myNet.begin(config.espnowNetName.c_str(), true);
myNet.begin(espnowNetName.c_str(), true);
if (config.workMode)
if (workMode)
{
// myNet.setCryptKey("VERY_LONG_CRYPT_KEY"); // If encryption is used, the key must be set same of all another ESP-NOW devices in network.
myNet.setOnBroadcastReceivingCallback(onEspnowMessage);
@ -144,7 +144,7 @@ void setup()
WiFi.softAP(("ESP-NOW gateway " + String(ESP.getEfuseMac(), HEX)).c_str(), "12345678");
#endif
if (config.workMode == ESP_NOW_WIFI)
if (workMode == ESP_NOW_WIFI)
{
uint8_t scan = WiFi.scanNetworks(false, false);
String name;
@ -161,24 +161,24 @@ void setup()
#if defined(ESP32)
WiFi.getNetworkInfo(i, name, encryption, rssi, bssid, channel);
#endif
if (name == config.ssid)
WiFi.begin(config.ssid.c_str(), config.password.c_str());
if (name == ssid)
WiFi.begin(ssid.c_str(), password.c_str());
}
}
if (config.workMode == ESP_NOW_WIFI)
if (workMode == ESP_NOW_WIFI)
{
ntpWiFiClient.begin();
mqttWifiClient.setBufferSize(2048);
mqttWifiClient.setServer(config.mqttHostName.c_str(), config.mqttHostPort);
mqttWifiClient.setServer(mqttHostName.c_str(), mqttHostPort);
mqttWifiClient.setCallback(onMqttMessage);
}
if (config.workMode == ESP_NOW_LAN)
if (workMode == ESP_NOW_LAN)
{
ntpEthClient.begin();
mqttEthClient.setBufferSize(2048);
mqttEthClient.setServer(config.mqttHostName.c_str(), config.mqttHostPort);
mqttEthClient.setServer(mqttHostName.c_str(), mqttHostPort);
mqttEthClient.setCallback(onMqttMessage);
}
@ -193,15 +193,15 @@ void setup()
void loop()
{
if (mqttAvailabilityCheckTimerSemaphore)
checkMqttAvailability();
if (keepAliveMessageTimerSemaphore)
sendKeepAliveMessage();
if (attributesMessageTimerSemaphore)
sendAttributesMessage();
if (config.workMode == ESP_NOW_WIFI)
if (mqttAvailabilityCheckTimerSemaphore)
checkMqttAvailability();
if (workMode == ESP_NOW_WIFI)
mqttWifiClient.loop();
if (config.workMode == ESP_NOW_LAN)
if (workMode == ESP_NOW_LAN)
mqttEthClient.loop();
myNet.maintenance();
ArduinoOTA.handle();
@ -214,187 +214,180 @@ void onEspnowMessage(const char *data, const uint8_t *sender)
esp_now_payload_data_t incomingData;
memcpy(&incomingData, data, sizeof(esp_now_payload_data_t));
if (incomingData.payloadsType == ENPT_ATTRIBUTES)
mqttPublish((config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/" + getValueName(incomingData.payloadsType)).c_str(), incomingData.message, true);
mqttPublish((topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/" + getValueName(incomingData.payloadsType)).c_str(), incomingData.message, true);
if (incomingData.payloadsType == ENPT_KEEP_ALIVE)
mqttPublish((config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/" + getValueName(incomingData.payloadsType)).c_str(), "online", true);
mqttPublish((topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/" + getValueName(incomingData.payloadsType)).c_str(), "online", true);
if (incomingData.payloadsType == ENPT_STATE)
mqttPublish((config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/" + getValueName(incomingData.payloadsType)).c_str(), incomingData.message, true);
mqttPublish((topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/" + getValueName(incomingData.payloadsType)).c_str(), incomingData.message, true);
if (incomingData.payloadsType == ENPT_CONFIG)
{
if (incomingData.deviceType == ENDT_SWITCH)
{
esp_now_payload_data_t configData;
memcpy(&configData.message, &incomingData.message, sizeof(esp_now_payload_data_t::message));
DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message));
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json;
deserializeJson(json, configData.message);
uint8_t unit = json[MCMT_DEVICE_UNIT].as<uint8_t>();
DynamicJsonDocument jsonConfig(2048); // Same as PubSubClient buffer size.
uint8_t unit = json["unit"].as<uint8_t>();
StaticJsonDocument<2048> jsonConfig;
jsonConfig["platform"] = "mqtt";
jsonConfig["name"] = json[MCMT_DEVICE_NAME];
jsonConfig["name"] = json["name"];
jsonConfig["unique_id"] = myNet.macToString(sender) + "-" + unit;
jsonConfig["device_class"] = getValueName(json[MCMT_DEVICE_CLASS].as<ha_switch_device_class_t>());
jsonConfig["state_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/state";
jsonConfig["value_template"] = "{{ value_json." + json[MCMT_VALUE_TEMPLATE].as<String>() + " }}";
jsonConfig["command_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/set";
jsonConfig["json_attributes_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/attributes";
jsonConfig["availability_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/status";
if (json[MCMT_PAYLOAD_ON])
jsonConfig["payload_on"] = json[MCMT_PAYLOAD_ON];
if (json[MCMT_PAYLOAD_OFF])
jsonConfig["payload_off"] = json[MCMT_PAYLOAD_OFF];
jsonConfig["device_class"] = getValueName(json["class"].as<ha_switch_device_class_t>());
jsonConfig["state_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/state";
jsonConfig["value_template"] = "{{ value_json." + json["template"].as<String>() + " }}";
jsonConfig["command_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/set";
jsonConfig["json_attributes_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/attributes";
jsonConfig["availability_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/status";
jsonConfig["payload_on"] = json["payload_on"];
jsonConfig["payload_off"] = json["payload_off"];
jsonConfig["optimistic"] = "false";
jsonConfig["retain"] = "true";
char buffer[2048]{0};
serializeJsonPretty(jsonConfig, buffer);
mqttPublish((config.topicPrefix + "/" + getValueName(json[MCMT_COMPONENT_TYPE].as<ha_component_type_t>()) + "/" + myNet.macToString(sender) + "-" + unit + "/config").c_str(), buffer, true);
mqttPublish((topicPrefix + "/" + getValueName(json["type"].as<ha_component_type_t>()) + "/" + myNet.macToString(sender) + "-" + unit + "/config").c_str(), buffer, true);
}
if (incomingData.deviceType == ENDT_LED)
{
esp_now_payload_data_t configData;
memcpy(&configData.message, &incomingData.message, sizeof(esp_now_payload_data_t::message));
DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message));
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json;
deserializeJson(json, configData.message);
uint8_t unit = json[MCMT_DEVICE_UNIT].as<uint8_t>();
esp_now_led_type_t ledClass = json[MCMT_DEVICE_CLASS];
DynamicJsonDocument jsonConfig(2048); // Same as PubSubClient buffer size.
uint8_t unit = json["unit"].as<uint8_t>();
esp_now_led_type_t ledClass = json["class"];
StaticJsonDocument<2048> jsonConfig;
jsonConfig["platform"] = "mqtt";
jsonConfig["name"] = json[MCMT_DEVICE_NAME];
jsonConfig["name"] = json["name"];
jsonConfig["unique_id"] = myNet.macToString(sender) + "-" + unit;
jsonConfig["state_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/state";
jsonConfig["state_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/state";
jsonConfig["state_value_template"] = "{{ value_json.state }}";
jsonConfig["command_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/set";
jsonConfig["brightness_state_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/state";
jsonConfig["command_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/set";
jsonConfig["brightness_state_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/state";
jsonConfig["brightness_value_template"] = "{{ value_json.brightness }}";
jsonConfig["brightness_command_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/brightness";
jsonConfig["brightness_command_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/brightness";
if (ledClass == ENLT_RGB || ledClass == ENLT_RGBW || ledClass == ENLT_RGBWW)
{
jsonConfig["rgb_state_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/state";
jsonConfig["rgb_state_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/state";
jsonConfig["rgb_value_template"] = "{{ value_json.rgb | join(',') }}";
jsonConfig["rgb_command_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/rgb";
jsonConfig["rgb_command_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/rgb";
}
if (ledClass == ENLT_WW || ledClass == ENLT_RGBWW)
{
jsonConfig["color_temp_state_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/state";
jsonConfig["color_temp_state_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/state";
jsonConfig["color_temp_value_template"] = "{{ value_json.temperature }}";
jsonConfig["color_temp_command_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/temperature";
jsonConfig["color_temp_command_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/temperature";
}
jsonConfig["json_attributes_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/attributes";
jsonConfig["availability_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/status";
if (json[MCMT_PAYLOAD_ON])
jsonConfig["payload_on"] = json[MCMT_PAYLOAD_ON];
if (json[MCMT_PAYLOAD_OFF])
jsonConfig["payload_off"] = json[MCMT_PAYLOAD_OFF];
jsonConfig["json_attributes_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/attributes";
jsonConfig["availability_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/status";
jsonConfig["payload_on"] = json["payload_on"];
jsonConfig["payload_off"] = json["payload_off"];
jsonConfig["optimistic"] = "false";
jsonConfig["retain"] = "true";
char buffer[2048]{0};
serializeJsonPretty(jsonConfig, buffer);
mqttPublish((config.topicPrefix + "/" + getValueName(json[MCMT_COMPONENT_TYPE].as<ha_component_type_t>()) + "/" + myNet.macToString(sender) + "-" + unit + "/config").c_str(), buffer, true);
mqttPublish((topicPrefix + "/" + getValueName(json["type"].as<ha_component_type_t>()) + "/" + myNet.macToString(sender) + "-" + unit + "/config").c_str(), buffer, true);
}
if (incomingData.deviceType == ENDT_SENSOR)
{
esp_now_payload_data_t configData;
memcpy(&configData.message, &incomingData.message, sizeof(esp_now_payload_data_t::message));
DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message));
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json;
deserializeJson(json, configData.message);
uint8_t unit = json[MCMT_DEVICE_UNIT].as<uint8_t>();
ha_component_type_t type = json[MCMT_COMPONENT_TYPE].as<ha_component_type_t>();
DynamicJsonDocument jsonConfig(2048); // Same as PubSubClient buffer size.
uint8_t unit = json["unit"].as<uint8_t>();
ha_component_type_t type = json["type"].as<ha_component_type_t>();
StaticJsonDocument<2048> jsonConfig;
jsonConfig["platform"] = "mqtt";
jsonConfig["name"] = json[MCMT_DEVICE_NAME];
jsonConfig["name"] = json["name"];
jsonConfig["unique_id"] = myNet.macToString(sender) + "-" + unit;
jsonConfig["state_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/state";
jsonConfig["value_template"] = "{{ value_json." + json[MCMT_VALUE_TEMPLATE].as<String>() + " }}";
jsonConfig["json_attributes_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/attributes";
jsonConfig["state_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/state";
jsonConfig["value_template"] = "{{ value_json." + json["template"].as<String>() + " }}";
jsonConfig["json_attributes_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/attributes";
jsonConfig["force_update"] = "true";
jsonConfig["retain"] = "true";
if (type == HACT_SENSOR)
{
jsonConfig["device_class"] = getValueName(json[MCMT_DEVICE_CLASS].as<ha_sensor_device_class_t>());
jsonConfig["unit_of_measurement"] = json[MCMT_UNIT_OF_MEASUREMENT];
}
jsonConfig["device_class"] = getValueName(json["class"].as<ha_sensor_device_class_t>());
if (type == HACT_BINARY_SENSOR)
jsonConfig["device_class"] = getValueName(json[MCMT_DEVICE_CLASS].as<ha_binary_sensor_device_class_t>());
if (json[MCMT_EXPIRE_AFTER])
jsonConfig["expire_after"] = json[MCMT_EXPIRE_AFTER];
if (json[MCMT_OFF_DELAY])
jsonConfig["off_delay"] = json[MCMT_OFF_DELAY];
if (json[MCMT_PAYLOAD_ON])
jsonConfig["payload_on"] = json[MCMT_PAYLOAD_ON];
if (json[MCMT_PAYLOAD_OFF])
jsonConfig["payload_off"] = json[MCMT_PAYLOAD_OFF];
{
ha_binary_sensor_device_class_t deviceClass = json["class"].as<ha_binary_sensor_device_class_t>();
if (deviceClass == HABSDC_BATTERY || deviceClass == HABSDC_WINDOW || deviceClass == HABSDC_DOOR)
jsonConfig["payload_off"] = json["payload_off"];
if (deviceClass == HABSDC_CONNECTIVITY)
jsonConfig["expire_after"] = json["expire_after"];
jsonConfig["device_class"] = getValueName(deviceClass);
jsonConfig["payload_on"] = json["payload_on"];
}
char buffer[2048]{0};
serializeJsonPretty(jsonConfig, buffer);
mqttPublish((config.topicPrefix + "/" + getValueName(type) + "/" + myNet.macToString(sender) + "-" + unit + "/config").c_str(), buffer, true);
mqttPublish((topicPrefix + "/" + getValueName(type) + "/" + myNet.macToString(sender) + "-" + unit + "/config").c_str(), buffer, true);
}
if (incomingData.deviceType == ENDT_RF_SENSOR)
{
esp_now_payload_data_t configData;
memcpy(&configData.message, &incomingData.message, sizeof(esp_now_payload_data_t::message));
DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message));
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json;
deserializeJson(json, configData.message);
uint8_t unit = json[MCMT_DEVICE_UNIT].as<uint8_t>();
ha_component_type_t haComponentType = json[MCMT_COMPONENT_TYPE].as<ha_component_type_t>();
rf_sensor_type_t rfSensorType = json[MCMT_RF_SENSOR_TYPE].as<rf_sensor_type_t>();
uint16_t rfSensorId = json[MCMT_RF_SENSOR_ID].as<uint16_t>();
String valueTemplate = json[MCMT_VALUE_TEMPLATE].as<String>();
DynamicJsonDocument jsonConfig(2048); // Same as PubSubClient buffer size.
uint8_t unit = json["u"].as<uint8_t>();
ha_component_type_t type = json["t"].as<ha_component_type_t>();
rf_sensor_type_t rf = json["r"].as<rf_sensor_type_t>();
uint16_t id = json["i"].as<uint16_t>();
String tmp = json["v"].as<String>();
StaticJsonDocument<2048> jsonConfig;
jsonConfig["platform"] = "mqtt";
jsonConfig["name"] = getValueName(rfSensorType) + " " + rfSensorId + " " + valueTemplate;
jsonConfig["unique_id"] = String(rfSensorId) + "-" + unit;
jsonConfig["state_topic"] = config.topicPrefix + "/rf_sensor/" + getValueName(rfSensorType) + "/" + rfSensorId + "/state";
jsonConfig["value_template"] = "{{ value_json." + valueTemplate + " }}";
jsonConfig["name"] = getValueName(rf) + " " + id + " " + tmp;
jsonConfig["unique_id"] = String(id) + "-" + unit;
jsonConfig["state_topic"] = topicPrefix + "/rf_sensor/" + getValueName(rf) + "/" + id + "/state";
jsonConfig["value_template"] = "{{ value_json." + tmp + " }}";
if (type == HACT_SENSOR)
{
jsonConfig["device_class"] = getValueName(json["h"].as<ha_sensor_device_class_t>());
jsonConfig["unit_of_measurement"] = json["m"];
jsonConfig["expire_after"] = json["e"];
}
if (type == HACT_BINARY_SENSOR)
{
ha_binary_sensor_device_class_t deviceClass = json["h"].as<ha_binary_sensor_device_class_t>();
if (deviceClass == HABSDC_MOISTURE)
jsonConfig["expire_after"] = json["e"];
jsonConfig["device_class"] = getValueName(deviceClass);
jsonConfig["payload_on"] = json["o"];
jsonConfig["payload_off"] = json["f"];
}
jsonConfig["force_update"] = "true";
jsonConfig["retain"] = "true";
if (haComponentType == HACT_SENSOR)
{
jsonConfig["device_class"] = getValueName(json[MCMT_DEVICE_CLASS].as<ha_sensor_device_class_t>());
jsonConfig["unit_of_measurement"] = json[MCMT_UNIT_OF_MEASUREMENT];
}
if (haComponentType == HACT_BINARY_SENSOR)
jsonConfig["device_class"] = getValueName(json[MCMT_DEVICE_CLASS].as<ha_binary_sensor_device_class_t>());
if (json[MCMT_EXPIRE_AFTER])
jsonConfig["expire_after"] = json[MCMT_EXPIRE_AFTER];
if (json[MCMT_OFF_DELAY])
jsonConfig["off_delay"] = json[MCMT_OFF_DELAY];
if (json[MCMT_PAYLOAD_ON])
jsonConfig["payload_on"] = json[MCMT_PAYLOAD_ON];
if (json[MCMT_PAYLOAD_OFF])
jsonConfig["payload_off"] = json[MCMT_PAYLOAD_OFF];
char buffer[2048]{0};
serializeJsonPretty(jsonConfig, buffer);
mqttPublish((config.topicPrefix + "/" + getValueName(haComponentType) + "/" + rfSensorId + "-" + unit + "/config").c_str(), buffer, true);
mqttPublish((topicPrefix + "/" + getValueName(type) + "/" + id + "-" + unit + "/config").c_str(), buffer, true);
}
if (incomingData.deviceType == ENDT_RF_GATEWAY)
{
esp_now_payload_data_t configData;
memcpy(&configData.message, &incomingData.message, sizeof(esp_now_payload_data_t::message));
DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message));
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json;
deserializeJson(json, configData.message);
uint8_t unit = json[MCMT_DEVICE_UNIT].as<uint8_t>();
DynamicJsonDocument jsonConfig(2048); // Same as PubSubClient buffer size.
uint8_t unit = json["unit"].as<uint8_t>();
StaticJsonDocument<2048> jsonConfig;
jsonConfig["platform"] = "mqtt";
jsonConfig["name"] = json[MCMT_DEVICE_NAME];
jsonConfig["name"] = json["name"];
jsonConfig["unique_id"] = myNet.macToString(sender) + "-" + unit;
jsonConfig["device_class"] = getValueName(json[MCMT_DEVICE_CLASS].as<ha_binary_sensor_device_class_t>());
jsonConfig["state_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/status";
jsonConfig["json_attributes_topic"] = config.topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/attributes";
jsonConfig["payload_on"] = json[MCMT_PAYLOAD_ON];
jsonConfig["expire_after"] = json[MCMT_EXPIRE_AFTER];
jsonConfig["device_class"] = getValueName(json["class"].as<ha_binary_sensor_device_class_t>());
jsonConfig["state_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/status";
jsonConfig["json_attributes_topic"] = topicPrefix + "/" + getValueName(incomingData.deviceType) + "/" + myNet.macToString(sender) + "/attributes";
jsonConfig["payload_on"] = json["payload_on"];
jsonConfig["expire_after"] = json["expire_after"];
jsonConfig["force_update"] = "true";
jsonConfig["retain"] = "true";
char buffer[2048]{0};
serializeJsonPretty(jsonConfig, buffer);
mqttPublish((config.topicPrefix + "/" + getValueName(json[MCMT_COMPONENT_TYPE].as<ha_component_type_t>()) + "/" + myNet.macToString(sender) + "-" + unit + "/config").c_str(), buffer, true);
mqttPublish((topicPrefix + "/" + getValueName(json["type"].as<ha_component_type_t>()) + "/" + myNet.macToString(sender) + "-" + unit + "/config").c_str(), buffer, true);
}
}
if (incomingData.payloadsType == ENPT_FORWARD)
{
esp_now_payload_data_t forwardData;
memcpy(&forwardData.message, &incomingData.message, sizeof(esp_now_payload_data_t::message));
DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message));
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json;
deserializeJson(json, forwardData.message);
if (incomingData.deviceType == ENDT_RF_GATEWAY)
mqttPublish((config.topicPrefix + "/rf_sensor/" + getValueName(json["type"].as<rf_sensor_type_t>()) + "/" + json["id"].as<uint16_t>() + "/state").c_str(), incomingData.message, false);
mqttPublish((topicPrefix + "/rf_sensor/" + getValueName(json["type"].as<rf_sensor_type_t>()) + "/" + json["id"].as<uint16_t>() + "/state").c_str(), incomingData.message, false);
}
}
@ -409,30 +402,29 @@ void onMqttMessage(char *topic, byte *payload, unsigned int length)
}
esp_now_payload_data_t outgoingData;
outgoingData.deviceType = ENDT_GATEWAY;
outgoingData.payloadsType = ENPT_SET;
DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message));
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json;
if (message == "update" || message == "restart")
{
if (mac == myNet.getNodeMac() && message == "restart")
ESP.restart();
flag = true;
}
if (String(topic) == config.topicPrefix + "/espnow_switch/" + mac + "/set" || String(topic) == config.topicPrefix + "/espnow_led/" + mac + "/set")
if (String(topic) == topicPrefix + "/espnow_switch/" + mac + "/set" || String(topic) == topicPrefix + "/espnow_led/" + mac + "/set")
{
flag = true;
json["set"] = message;
}
if (String(topic) == config.topicPrefix + "/espnow_led/" + mac + "/brightness")
if (String(topic) == topicPrefix + "/espnow_led/" + mac + "/brightness")
{
flag = true;
json["brightness"] = message;
}
if (String(topic) == config.topicPrefix + "/espnow_led/" + mac + "/temperature")
if (String(topic) == topicPrefix + "/espnow_led/" + mac + "/temperature")
{
flag = true;
json["temperature"] = message;
}
if (String(topic) == config.topicPrefix + "/espnow_led/" + mac + "/rgb")
if (String(topic) == topicPrefix + "/espnow_led/" + mac + "/rgb")
{
flag = true;
json["rgb"] = message;
@ -441,8 +433,8 @@ void onMqttMessage(char *topic, byte *payload, unsigned int length)
{
if (message == "restart")
outgoingData.payloadsType = ENPT_RESTART;
if (message == "update")
outgoingData.payloadsType = ENPT_UPDATE;
else
outgoingData.payloadsType = message == "update" ? ENPT_UPDATE : ENPT_SET;
serializeJsonPretty(json, outgoingData.message);
char temp[sizeof(esp_now_payload_data_t)]{0};
memcpy(&temp, &outgoingData, sizeof(esp_now_payload_data_t));
@ -456,14 +448,14 @@ void sendKeepAliveMessage()
{
keepAliveMessageTimerSemaphore = false;
if (isMqttAvailable)
mqttPublish((config.topicPrefix + "/espnow_gateway/" + myNet.getNodeMac() + "/status").c_str(), "online", true);
mqttPublish((topicPrefix + "/espnow_gateway/" + myNet.getNodeMac() + "/status").c_str(), "online", true);
esp_now_payload_data_t outgoingData;
outgoingData.deviceType = ENDT_GATEWAY;
outgoingData.payloadsType = ENPT_KEEP_ALIVE;
DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message));
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json;
json["MQTT"] = isMqttAvailable ? "online" : "offline";
json["frequency"] = 10; // For compatibility with the previous version. Will be removed in future releases.
if (config.workMode == ESP_NOW_WIFI && WiFi.isConnected())
if (workMode == ESP_NOW_WIFI)
{
ntpWiFiClient.update();
uint64_t epochTime = ntpWiFiClient.getEpochTime();
@ -471,7 +463,7 @@ void sendKeepAliveMessage()
json["time"] = ntpWiFiClient.getFormattedTime();
json["date"] = String(time->tm_mday) + "." + String(time->tm_mon + 1) + "." + String(time->tm_year + 1900);
}
if (config.workMode == ESP_NOW_LAN && Ethernet.linkStatus() == LinkON)
if (workMode == ESP_NOW_LAN)
{
ntpEthClient.update();
uint64_t epochTime = ntpEthClient.getEpochTime();
@ -496,7 +488,7 @@ void sendAttributesMessage()
uint32_t mins = secs / 60;
uint32_t hours = mins / 60;
uint32_t days = hours / 24;
DynamicJsonDocument json(sizeof(esp_now_payload_data_t::message));
StaticJsonDocument<sizeof(esp_now_payload_data_t::message)> json;
json["Type"] = "ESP-NOW gateway";
#if defined(ESP8266)
json["MCU"] = "ESP8266";
@ -507,32 +499,32 @@ void sendAttributesMessage()
json["MAC"] = myNet.getNodeMac();
json["Firmware"] = firmware;
json["Library"] = myNet.getFirmwareVersion();
if (config.workMode == ESP_NOW_WIFI)
if (workMode == ESP_NOW_WIFI)
json["IP"] = WiFi.localIP().toString();
if (config.workMode == ESP_NOW_LAN)
if (workMode == ESP_NOW_LAN)
json["IP"] = Ethernet.localIP().toString();
json["Uptime"] = "Days:" + String(days) + " Hours:" + String(hours - (days * 24)) + " Mins:" + String(mins - (hours * 60));
char buffer[sizeof(esp_now_payload_data_t::message)]{0};
serializeJsonPretty(json, buffer);
mqttPublish((config.topicPrefix + "/espnow_gateway/" + myNet.getNodeMac() + "/attributes").c_str(), buffer, true);
mqttPublish((topicPrefix + "/espnow_gateway/" + myNet.getNodeMac() + "/attributes").c_str(), buffer, true);
}
void sendConfigMessage()
{
DynamicJsonDocument json(2048); // Same as PubSubClient buffer size.
StaticJsonDocument<1024> json;
json["platform"] = "mqtt";
json["name"] = config.deviceName;
json["name"] = deviceName;
json["unique_id"] = myNet.getNodeMac() + "-1";
json["device_class"] = "connectivity";
json["state_topic"] = config.topicPrefix + "/espnow_gateway/" + myNet.getNodeMac() + "/status";
json["json_attributes_topic"] = config.topicPrefix + "/espnow_gateway/" + myNet.getNodeMac() + "/attributes";
json["state_topic"] = topicPrefix + "/espnow_gateway/" + myNet.getNodeMac() + "/status";
json["json_attributes_topic"] = topicPrefix + "/espnow_gateway/" + myNet.getNodeMac() + "/attributes";
json["payload_on"] = "online";
json["expire_after"] = 30;
json["force_update"] = "true";
json["retain"] = "true";
char buffer[1024]{0};
serializeJsonPretty(json, buffer);
mqttPublish((config.topicPrefix + "/binary_sensor/" + myNet.getNodeMac() + "-1" + "/config").c_str(), buffer, true);
mqttPublish((topicPrefix + "/binary_sensor/" + myNet.getNodeMac() + "-1" + "/config").c_str(), buffer, true);
}
String getValue(String data, char separator, uint8_t index)
@ -552,27 +544,47 @@ String getValue(String data, char separator, uint8_t index)
void loadConfig()
{
EEPROM.begin(4096);
if (EEPROM.read(4095) == 254)
{
EEPROM.get(0, config);
EEPROM.end();
}
else
{
EEPROM.end();
if (!LittleFS.exists("/config.json"))
saveConfig();
}
delay(50);
File file = LittleFS.open("/config.json", "r");
String jsonFile = file.readString();
StaticJsonDocument<1024> json;
deserializeJson(json, jsonFile);
espnowNetName = json["espnowNetName"].as<String>();
deviceName = json["deviceName"].as<String>();
ssid = json["ssid"].as<String>();
password = json["password"].as<String>();
mqttHostName = json["mqttHostName"].as<String>();
mqttHostPort = json["mqttHostPort"];
mqttUserLogin = json["mqttUserLogin"].as<String>();
mqttUserPassword = json["mqttUserPassword"].as<String>();
topicPrefix = json["topicPrefix"].as<String>();
workMode = json["workMode"];
ntpHostName = json["ntpHostName"].as<String>();
gmtOffset = json["gmtOffset"];
file.close();
}
void saveConfig()
{
EEPROM.begin(4096);
EEPROM.write(4095, 254);
EEPROM.put(0, config);
EEPROM.end();
delay(50);
StaticJsonDocument<1024> json;
json["firmware"] = firmware;
json["espnowNetName"] = espnowNetName;
json["deviceName"] = deviceName;
json["ssid"] = ssid;
json["password"] = password;
json["mqttHostName"] = mqttHostName;
json["mqttHostPort"] = mqttHostPort;
json["mqttUserLogin"] = mqttUserLogin;
json["mqttUserPassword"] = mqttUserPassword;
json["topicPrefix"] = topicPrefix;
json["workMode"] = workMode;
json["ntpHostName"] = ntpHostName;
json["gmtOffset"] = gmtOffset;
json["system"] = "empty";
File file = LittleFS.open("/config.json", "w");
serializeJsonPretty(json, file);
file.close();
}
String xmlNode(String tags, String data)
@ -594,7 +606,7 @@ void setupWebServer()
ssdpHeader = xmlNode("specVersion", ssdpHeader);
ssdpHeader += xmlNode("URLBase", "http://" + WiFi.localIP().toString());
String ssdpDescription = xmlNode("deviceType", "upnp:rootdevice");
ssdpDescription += xmlNode("friendlyName", config.deviceName);
ssdpDescription += xmlNode("friendlyName", deviceName);
ssdpDescription += xmlNode("presentationURL", "/");
ssdpDescription += xmlNode("serialNumber", "0000000" + String(random(1000)));
ssdpDescription += xmlNode("modelName", "ESP-NOW gateway");
@ -612,57 +624,38 @@ void setupWebServer()
webServer.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
{ request->send(LittleFS, "/index.htm"); });
webServer.on("/function.js", HTTP_GET, [](AsyncWebServerRequest *request)
{ request->send(LittleFS, "/function.js"); });
webServer.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request)
{ request->send(LittleFS, "/style.css"); });
webServer.on("/setting", HTTP_GET, [](AsyncWebServerRequest *request)
{
config.ssid = request->getParam("ssid")->value();
config.password = request->getParam("password")->value();
config.mqttHostName = request->getParam("mqttHostName")->value();
config.mqttHostPort = request->getParam("mqttHostPort")->value().toInt();
config.mqttUserLogin = request->getParam("mqttUserLogin")->value();
config.mqttUserPassword = request->getParam("mqttUserPassword")->value();
config.topicPrefix = request->getParam("topicPrefix")->value();
config.deviceName = request->getParam("deviceName")->value();
config.espnowNetName = request->getParam("espnowNetName")->value();
config.workMode = request->getParam("workMode")->value().toInt();
config.ntpHostName = request->getParam("ntpHostName")->value();
config.gmtOffset = request->getParam("gmtOffset")->value().toInt();
ssid = request->getParam("ssid")->value();
password = request->getParam("password")->value();
mqttHostName = request->getParam("host")->value();
mqttHostPort = request->getParam("port")->value().toInt();
mqttUserLogin = request->getParam("login")->value();
mqttUserPassword = request->getParam("pass")->value();
topicPrefix = request->getParam("prefix")->value();
deviceName = request->getParam("name")->value();
espnowNetName = request->getParam("net")->value();
workMode = request->getParam("mode")->value().toInt();
ntpHostName = request->getParam("ntp")->value();
gmtOffset = request->getParam("zone")->value().toInt();
request->send(200);
saveConfig(); });
webServer.on("/config", HTTP_GET, [](AsyncWebServerRequest *request)
{
String configJson;
DynamicJsonDocument json(2048); // For overflow protection.
json["firmware"] = firmware;
json["espnowNetName"] = config.espnowNetName;
json["deviceName"] = config.deviceName;
json["ssid"] = config.ssid;
json["password"] = config.password;
json["mqttHostName"] = config.mqttHostName;
json["mqttHostPort"] = config.mqttHostPort;
json["mqttUserLogin"] = config.mqttUserLogin;
json["mqttUserPassword"] = config.mqttUserPassword;
json["topicPrefix"] = config.topicPrefix;
json["workMode"] = config.workMode;
json["ntpHostName"] = config.ntpHostName;
json["gmtOffset"] = config.gmtOffset;
serializeJsonPretty(json, configJson);
request->send(200, "application/json", configJson); });
webServer.on("/restart", HTTP_GET, [](AsyncWebServerRequest *request)
{request->send(200);
{
request->send(200);
ESP.restart(); });
webServer.onNotFound([](AsyncWebServerRequest *request)
{ request->send(404, "text/plain", "File Not Found"); });
{
if (LittleFS.exists(request->url()))
request->send(LittleFS, request->url());
else
{
request->send(404, "text/plain", "File Not Found");
} });
if (config.workMode == ESP_NOW_WIFI)
if (workMode == ESP_NOW_WIFI)
SSDP.begin();
webServer.begin();
@ -672,18 +665,18 @@ void checkMqttAvailability()
{
mqttAvailabilityCheckTimerSemaphore = false;
if (config.workMode == ESP_NOW_WIFI)
if (workMode == ESP_NOW_WIFI)
if (WiFi.isConnected())
if (!mqttWifiClient.connected())
{
isMqttAvailable = false;
if (mqttWifiClient.connect(mqttUserID, config.mqttUserLogin.c_str(), config.mqttUserPassword.c_str()))
if (mqttWifiClient.connect(mqttUserID, mqttUserLogin.c_str(), mqttUserPassword.c_str()))
{
isMqttAvailable = true;
mqttWifiClient.subscribe((config.topicPrefix + "/espnow_gateway/#").c_str());
mqttWifiClient.subscribe((config.topicPrefix + "/espnow_switch/#").c_str());
mqttWifiClient.subscribe((config.topicPrefix + "/espnow_led/#").c_str());
mqttWifiClient.subscribe((topicPrefix + "/espnow_gateway/#").c_str());
mqttWifiClient.subscribe((topicPrefix + "/espnow_switch/#").c_str());
mqttWifiClient.subscribe((topicPrefix + "/espnow_led/#").c_str());
sendConfigMessage();
sendAttributesMessage();
@ -691,18 +684,18 @@ void checkMqttAvailability()
}
}
if (config.workMode == ESP_NOW_LAN)
if (workMode == ESP_NOW_LAN)
if (Ethernet.linkStatus() == LinkON)
if (!mqttEthClient.connected())
{
isMqttAvailable = false;
if (mqttEthClient.connect(mqttUserID, config.mqttUserLogin.c_str(), config.mqttUserPassword.c_str()))
if (mqttEthClient.connect(mqttUserID, mqttUserLogin.c_str(), mqttUserPassword.c_str()))
{
isMqttAvailable = true;
mqttEthClient.subscribe((config.topicPrefix + "/espnow_gateway/#").c_str());
mqttEthClient.subscribe((config.topicPrefix + "/espnow_switch/#").c_str());
mqttEthClient.subscribe((config.topicPrefix + "/espnow_led/#").c_str());
mqttEthClient.subscribe((topicPrefix + "/espnow_gateway/#").c_str());
mqttEthClient.subscribe((topicPrefix + "/espnow_switch/#").c_str());
mqttEthClient.subscribe((topicPrefix + "/espnow_led/#").c_str());
sendConfigMessage();
sendAttributesMessage();
@ -713,9 +706,9 @@ void checkMqttAvailability()
void mqttPublish(const char *topic, const char *payload, bool retained)
{
if (config.workMode == ESP_NOW_WIFI)
if (workMode == ESP_NOW_WIFI)
mqttWifiClient.publish(topic, payload, retained);
if (config.workMode == ESP_NOW_LAN)
if (workMode == ESP_NOW_LAN)
mqttEthClient.publish(topic, payload, retained);
}