Browse Source

Home Assistant updates (#1885)

- remove "platform" key, see #1440. this implicitly sets schema to "basic". pending some other clean-up regarding json and mqtt queueing, other schema can be added down the line 
- updated ws module queue elem to capture callbacks list, allows to pass more than one callback (for example, when they are generated on the fly as lambdas, see ha wsPost usage)
- modified method to send ha config to use global ws queue, fix #1762 problem with empty topics and ensure json allocation is consistent.
- use existing defines to set mqtt payload options. amend #1085, #1188, #1883 to use the set payload value. drop HOMEASSISTANT_PAYLOAD... defines. 
- update MQTT_STATUS_ONLINE/OFFLINE and RELAY_MQTT_ON/OFF with runtime configuration
- filter payload strings so that the resulting yaml value is not interpreted as bool (python True, False)
- helper method for settings to streamline string values manipulation
master
Max Prokhorov 5 years ago
committed by GitHub
parent
commit
13c1a19328
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 297 additions and 158 deletions
  1. +18
    -1
      code/espurna/config/deprecated.h
  2. +4
    -18
      code/espurna/config/general.h
  3. +67
    -22
      code/espurna/config/prototypes.h
  4. +124
    -108
      code/espurna/homeassistant.ino
  5. +30
    -4
      code/espurna/mqtt.ino
  6. +29
    -3
      code/espurna/relay.ino
  7. +11
    -0
      code/espurna/settings.ino
  8. +2
    -2
      code/espurna/utils.ino
  9. +12
    -0
      code/espurna/ws.ino

+ 18
- 1
code/espurna/config/deprecated.h View File

@ -24,4 +24,21 @@
#ifdef EVENTS_PIN #ifdef EVENTS_PIN
#warning EVENTS_PIN is deprecated! Please use EVENTS1_PIN instead #warning EVENTS_PIN is deprecated! Please use EVENTS1_PIN instead
#define EVENTS1_PIN EVENTS_PIN #define EVENTS1_PIN EVENTS_PIN
#endif
#endif
// 1.13.6 unifies mqtt payload options
#ifdef HOMEASSISTANT_PAYLOAD_ON
#warning HOMEASSISTANT_PAYLOAD_ON is deprecated! Global RELAY_MQTT_ON is used instead
#endif
#ifdef HOMEASSISTANT_PAYLOAD_OFF
#warning HOMEASSISTANT_PAYLOAD_OFF is deprecated! Global RELAY_MQTT_OFF is used instead
#endif
#ifdef HOMEASSISTANT_PAYLOAD_AVAILABLE
#warning HOMEASSISTANT_PAYLOAD_AVAILABLE is deprecated! Global MQTT_STATUS_ONLINE is used instead
#endif
#ifdef HOMEASSISTANT_PAYLOAD_NOT_AVAILABLE
#warning HOMEASSISTANT_PAYLOAD_NOT_AVAILABLE is deprecated! Global MQTT_STATUS_OFFLINE is used instead
#endif

+ 4
- 18
code/espurna/config/general.h View File

@ -408,6 +408,7 @@
#ifndef RELAY_MQTT_ON #ifndef RELAY_MQTT_ON
#define RELAY_MQTT_ON "1" #define RELAY_MQTT_ON "1"
#endif #endif
#ifndef RELAY_MQTT_OFF #ifndef RELAY_MQTT_OFF
#define RELAY_MQTT_OFF "0" #define RELAY_MQTT_OFF "0"
#endif #endif
@ -1076,15 +1077,16 @@
#define MQTT_TOPIC_NOTIFY_TEMP_RANGE_MAX "notify_temp_range_max" #define MQTT_TOPIC_NOTIFY_TEMP_RANGE_MAX "notify_temp_range_max"
#ifndef MQTT_STATUS_ONLINE
#define MQTT_STATUS_ONLINE "1" // Value for the device ON message #define MQTT_STATUS_ONLINE "1" // Value for the device ON message
#endif
#ifndef MQTT_STATUS_OFFLINE #ifndef MQTT_STATUS_OFFLINE
#define MQTT_STATUS_OFFLINE "0" // Value for the device OFF message (will) #define MQTT_STATUS_OFFLINE "0" // Value for the device OFF message (will)
#endif #endif
#define MQTT_ACTION_RESET "reboot" // RESET MQTT topic particle #define MQTT_ACTION_RESET "reboot" // RESET MQTT topic particle
#define MQTT_MESSAGE_ID_SHIFT 1000 // Store MQTT message id into EEPROM every these many
// Custom get and set postfixes // Custom get and set postfixes
// Use something like "/status" or "/set", with leading slash // Use something like "/status" or "/set", with leading slash
// Since 1.9.0 the default value is "" for getter and "/set" for setter // Since 1.9.0 the default value is "" for getter and "/set" for setter
@ -1286,22 +1288,6 @@
#define HOMEASSISTANT_PREFIX "homeassistant" // Default MQTT prefix #define HOMEASSISTANT_PREFIX "homeassistant" // Default MQTT prefix
#endif #endif
#ifndef HOMEASSISTANT_PAYLOAD_ON
#define HOMEASSISTANT_PAYLOAD_ON "1" // Payload for ON and available messages
#endif
#ifndef HOMEASSISTANT_PAYLOAD_OFF
#define HOMEASSISTANT_PAYLOAD_OFF "0" // Payload for OFF and unavailable messages
#endif
#ifndef HOMEASSISTANT_PAYLOAD_AVAILABLE
#define HOMEASSISTANT_PAYLOAD_AVAILABLE "1" // Payload for available messages
#endif
#ifndef HOMEASSISTANT_PAYLOAD_NOT_AVAILABLE
#define HOMEASSISTANT_PAYLOAD_NOT_AVAILABLE "0" // Payload for available messages
#endif
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// INFLUXDB // INFLUXDB
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------


+ 67
- 22
code/espurna/config/prototypes.h View File

@ -194,10 +194,28 @@ void lightChannel(unsigned char id, unsigned char value);
using mqtt_callback_f = std::function<void(unsigned int, const char *, char *)>; using mqtt_callback_f = std::function<void(unsigned int, const char *, char *)>;
#if MQTT_SUPPORT
void mqttRegister(mqtt_callback_f callback);
String mqttMagnitude(char * topic);
#endif
void mqttRegister(mqtt_callback_f callback);
String mqttTopic(const char * magnitude, bool is_set);
String mqttTopic(const char * magnitude, unsigned int index, bool is_set);
String mqttMagnitude(char * topic);
void mqttSendRaw(const char * topic, const char * message, bool retain);
void mqttSendRaw(const char * topic, const char * message);
void mqttSend(const char * topic, const char * message, bool force, bool retain);
void mqttSend(const char * topic, const char * message, bool force);
void mqttSend(const char * topic, const char * message);
void mqttSend(const char * topic, unsigned int index, const char * message, bool force);
void mqttSend(const char * topic, unsigned int index, const char * message);
const String& mqttPayloadOnline();
const String& mqttPayloadOffline();
const char* mqttPayloadStatus(bool status);
void mqttSendStatus();
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// OTA // OTA
@ -236,10 +254,25 @@ typedef struct {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#include <bitset> #include <bitset>
bool relayStatus(unsigned char id, bool status, bool report, bool group_report);
bool relayStatus(unsigned char id, bool status);
bool relayStatus(unsigned char id);
void relayToggle(unsigned char id, bool report, bool group_report);
void relayToggle(unsigned char id);
unsigned char relayCount();
unsigned char relayParsePayload(const char * payload);
const String& relayPayloadOn();
const String& relayPayloadOff();
const char* relayPayload(bool status);
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Settings // Settings
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#include <Embedis.h> #include <Embedis.h>
template<typename T> bool setSetting(const String& key, T value); template<typename T> bool setSetting(const String& key, T value);
template<typename T> bool setSetting(const String& key, unsigned int index, T value); template<typename T> bool setSetting(const String& key, unsigned int index, T value);
template<typename T> String getSetting(const String& key, T defaultValue); template<typename T> String getSetting(const String& key, T defaultValue);
@ -247,6 +280,17 @@ template<typename T> String getSetting(const String& key, unsigned int index, T
void settingsGetJson(JsonObject& data); void settingsGetJson(JsonObject& data);
bool settingsRestoreJson(JsonObject& data); bool settingsRestoreJson(JsonObject& data);
struct settings_cfg_t {
String& setting;
const char* key;
const char* default_value;
};
using settings_filter_t = std::function<String(String& value)>;
using settings_cfg_list_t = std::initializer_list<settings_cfg_t>;
void settingsProcessConfig(const settings_cfg_list_t& config, settings_filter_t filter = nullptr);
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Terminal // Terminal
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -302,9 +346,9 @@ struct ws_data_t;
struct ws_debug_t; struct ws_debug_t;
struct ws_callbacks_t; struct ws_callbacks_t;
using ws_on_send_callback_f = std::function<void(JsonObject&)>;
using ws_on_action_callback_f = std::function<void(uint32_t, const char *, JsonObject&)>;
using ws_on_keycheck_callback_f = std::function<bool(const char *, JsonVariant&)>;
using ws_on_send_callback_f = std::function<void(JsonObject& root)>;
using ws_on_action_callback_f = std::function<void(uint32_t client_id, const char * action, JsonObject& data)>;
using ws_on_keycheck_callback_f = std::function<bool(const char * key, JsonVariant& value)>;
using ws_on_send_callback_list_t = std::vector<ws_on_send_callback_f>; using ws_on_send_callback_list_t = std::vector<ws_on_send_callback_f>;
using ws_on_action_callback_list_t = std::vector<ws_on_action_callback_f>; using ws_on_action_callback_list_t = std::vector<ws_on_action_callback_f>;
@ -329,27 +373,28 @@ using ws_on_keycheck_callback_list_t = std::vector<ws_on_keycheck_callback_f>;
ws_callbacks_t& wsRegister(); ws_callbacks_t& wsRegister();
void wsSetup(); void wsSetup();
void wsSend(uint32_t, const char*);
void wsSend(uint32_t, JsonObject&);
void wsSend(JsonObject&);
void wsSend(ws_on_send_callback_f);
void wsSend(uint32_t client_id, const char* data);
void wsSend(uint32_t client_id, JsonObject& root);
void wsSend(JsonObject& root);
void wsSend(ws_on_send_callback_f callback);
void wsSend_P(PGM_P);
void wsSend_P(uint32_t, PGM_P);
void wsSend_P(PGM_P data);
void wsSend_P(uint32_t client_id, PGM_P data);
void wsPost(const ws_on_send_callback_f&);
void wsPost(const ws_on_send_callback_list_t&);
void wsPost(uint32_t, const ws_on_send_callback_list_t&);
void wsPost(const ws_on_send_callback_f& callback);
void wsPost(const ws_on_send_callback_list_t& callbacks);
void wsPost(uint32_t client_id, const ws_on_send_callback_list_t& callbacks);
void wsPostAll(uint32_t, const ws_on_send_callback_list_t&);
void wsPostAll(const ws_on_send_callback_list_t&);
void wsPostAll(uint32_t client_id, const ws_on_send_callback_list_t& callbacks);
void wsPostAll(const ws_on_send_callback_list_t& callbacks);
void wsPostSequence(uint32_t, const ws_on_send_callback_list_t&);
void wsPostSequence(const ws_on_send_callback_list_t&);
void wsPostSequence(uint32_t client_id, const ws_on_send_callback_list_t& callbacks);
void wsPostSequence(uint32_t client_id, ws_on_send_callback_list_t&& callbacks);
void wsPostSequence(const ws_on_send_callback_list_t& callbacks);
bool wsConnected(); bool wsConnected();
bool wsConnected(uint32_t);
bool wsDebugSend(const char*, const char*);
bool wsConnected(uint32_t client_id);
bool wsDebugSend(const char* prefix, const char* message);
#endif #endif
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------


+ 124
- 108
code/espurna/homeassistant.ino View File

@ -9,7 +9,6 @@ Copyright (C) 2017-2019 by Xose Pérez <xose dot perez at gmail dot com>
#if HOMEASSISTANT_SUPPORT #if HOMEASSISTANT_SUPPORT
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <queue>
bool _haEnabled = false; bool _haEnabled = false;
bool _haSendFlag = false; bool _haSendFlag = false;
@ -18,7 +17,29 @@ bool _haSendFlag = false;
// UTILS // UTILS
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
String _haFixName(String name) {
// per yaml 1.1 spec, following scalars are converted to bool. we want the string, so quoting the output
// y|Y|yes|Yes|YES|n|N|no|No|NO |true|True|TRUE|false|False|FALSE |on|On|ON|off|Off|OFF
String _haFixPayload(const String& value) {
if (value.equalsIgnoreCase("y")
|| value.equalsIgnoreCase("n")
|| value.equalsIgnoreCase("yes")
|| value.equalsIgnoreCase("no")
|| value.equalsIgnoreCase("true")
|| value.equalsIgnoreCase("false")
|| value.equalsIgnoreCase("on")
|| value.equalsIgnoreCase("off")
) {
String temp;
temp.reserve(value.length() + 2);
temp = "\"";
temp += value;
temp += "\"";
return temp;
}
return value;
}
String& _haFixName(String& name) {
for (unsigned char i=0; i<name.length(); i++) { for (unsigned char i=0; i<name.length(); i++) {
if (!isalnum(name.charAt(i))) name.setCharAt(i, '_'); if (!isalnum(name.charAt(i))) name.setCharAt(i, '_');
} }
@ -74,7 +95,6 @@ void _haSendMagnitude(unsigned char i, JsonObject& config) {
unsigned char type = magnitudeType(i); unsigned char type = magnitudeType(i);
config["name"] = _haFixName(getSetting("hostname") + String(" ") + magnitudeTopic(type)); config["name"] = _haFixName(getSetting("hostname") + String(" ") + magnitudeTopic(type));
config.set("platform", "mqtt");
config["state_topic"] = mqttTopic(magnitudeTopicIndex(i).c_str(), false); config["state_topic"] = mqttTopic(magnitudeTopicIndex(i).c_str(), false);
config["unit_of_measurement"] = magnitudeUnits(type); config["unit_of_measurement"] = magnitudeUnits(type);
} }
@ -102,7 +122,7 @@ void _haSendMagnitudes(ha_config_t& config) {
} }
mqttSend(MQTT_TOPIC_STATUS, MQTT_STATUS_ONLINE, true);
mqttSendStatus();
} }
@ -120,16 +140,15 @@ void _haSendSwitch(unsigned char i, JsonObject& config) {
} }
config.set("name", _haFixName(name)); config.set("name", _haFixName(name));
config.set("platform", "mqtt");
if (relayCount()) { if (relayCount()) {
config["state_topic"] = mqttTopic(MQTT_TOPIC_RELAY, i, false); config["state_topic"] = mqttTopic(MQTT_TOPIC_RELAY, i, false);
config["command_topic"] = mqttTopic(MQTT_TOPIC_RELAY, i, true); config["command_topic"] = mqttTopic(MQTT_TOPIC_RELAY, i, true);
config["payload_on"] = String(HOMEASSISTANT_PAYLOAD_ON);
config["payload_off"] = String(HOMEASSISTANT_PAYLOAD_OFF);
config["payload_on"] = relayPayload(true);
config["payload_off"] = relayPayload(false);
config["availability_topic"] = mqttTopic(MQTT_TOPIC_STATUS, false); config["availability_topic"] = mqttTopic(MQTT_TOPIC_STATUS, false);
config["payload_available"] = String(HOMEASSISTANT_PAYLOAD_AVAILABLE);
config["payload_not_available"] = String(HOMEASSISTANT_PAYLOAD_NOT_AVAILABLE);
config["payload_available"] = mqttPayloadStatus(true);
config["payload_not_available"] = mqttPayloadStatus(false);
} }
#if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE #if LIGHT_PROVIDER != LIGHT_PROVIDER_NONE
@ -178,109 +197,93 @@ void _haSendSwitches(ha_config_t& config) {
} }
mqttSendRaw(topic.c_str(), output.c_str()); mqttSendRaw(topic.c_str(), output.c_str());
mqttSend(MQTT_TOPIC_STATUS, MQTT_STATUS_ONLINE, true);
} }
mqttSendStatus();
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void _haDumpConfig(std::function<void(String&)> printer, bool wrapJson = false) {
constexpr const size_t HA_YAML_BUFFER_SIZE = 1024;
constexpr const size_t BUFFER_SIZE = 1024;
void _haSwitchYaml(unsigned char index, JsonObject& root) {
String output; String output;
output.reserve(BUFFER_SIZE + 64);
DynamicJsonBuffer jsonBuffer(BUFFER_SIZE);
output.reserve(HA_YAML_BUFFER_SIZE);
for (unsigned char i=0; i<relayCount(); i++) {
JsonObject& config = root.createNestedObject("config");
_haSendSwitch(index, config);
JsonObject& config = jsonBuffer.createObject();
_haSendSwitch(i, config);
if (index == 0) output += "\n\n" + switchType + ":";
output += "\n";
bool first = true;
if (wrapJson) {
output += "{\"haConfig\": \"";
for (auto kv : config) {
if (first) {
output += " - ";
first = false;
} else {
output += " ";
} }
output += "\n\n" + switchType + ":\n";
bool first = true;
for (auto kv : config) {
if (first) {
output += " - ";
first = false;
} else {
output += " ";
}
output += kv.key;
output += ": ";
output += kv.key;
output += ": ";
if (strncmp(kv.key, "payload_", strlen("payload_")) == 0) {
output += _haFixPayload(kv.value.as<String>());
} else {
output += kv.value.as<String>(); output += kv.value.as<String>();
output += "\n";
}
output += " ";
if (wrapJson) {
output += "\"}";
} }
jsonBuffer.clear();
printer(output);
output = "";
output += "\n";
} }
output += " ";
#if SENSOR_SUPPORT
for (unsigned char i=0; i<magnitudeCount(); i++) {
root.remove("config");
root["haConfig"] = output;
}
JsonObject& config = jsonBuffer.createObject();
_haSendMagnitude(i, config);
#if SENSOR_SUPPORT
if (wrapJson) {
output += "{\"haConfig\": \"";
}
void _haSensorYaml(unsigned char index, JsonObject& root) {
output += "\n\nsensor:\n";
bool first = true;
for (auto kv : config) {
if (first) {
output += " - ";
first = false;
} else {
output += " ";
}
String value = kv.value.as<String>();
value.replace("%", "'%'");
output += kv.key;
output += ": ";
output += value;
output += "\n";
}
output += " ";
String output;
output.reserve(HA_YAML_BUFFER_SIZE);
if (wrapJson) {
output += "\"}";
}
JsonObject& config = root.createNestedObject("config");
_haSendMagnitude(index, config);
jsonBuffer.clear();
printer(output);
output = "";
if (index == 0) output += "\n\nsensor:";
output += "\n";
bool first = true;
for (auto kv : config) {
if (first) {
output += " - ";
first = false;
} else {
output += " ";
} }
String value = kv.value.as<String>();
value.replace("%", "'%'");
output += kv.key;
output += ": ";
output += value;
output += "\n";
}
output += " ";
root.remove("config");
root["haConfig"] = output;
#endif
} }
#endif // SENSOR_SUPPORT
void _haGetDeviceConfig(JsonObject& config) { void _haGetDeviceConfig(JsonObject& config) {
String identifier = getIdentifier();
config.createNestedArray("identifiers").add(identifier);
config.createNestedArray("identifiers").add(getIdentifier());
config["name"] = getSetting("desc", getSetting("hostname")); config["name"] = getSetting("desc", getSetting("hostname"));
config["manufacturer"] = String(MANUFACTURER);
config["model"] = String(DEVICE);
config["sw_version"] = String(APP_NAME) + " " + String(APP_VERSION) + " (" + getCoreVersion() + ")";
config["manufacturer"] = MANUFACTURER;
config["model"] = DEVICE;
config["sw_version"] = String(APP_NAME) + " " + APP_VERSION + " (" + getCoreVersion() + ")";
} }
void _haSend() { void _haSend() {
@ -315,8 +318,6 @@ void _haConfigure() {
#if WEB_SUPPORT #if WEB_SUPPORT
std::queue<uint32_t> _ha_send_config;
bool _haWebSocketOnKeyCheck(const char * key, JsonVariant& value) { bool _haWebSocketOnKeyCheck(const char * key, JsonVariant& value) {
return (strncmp(key, "ha", 2) == 0); return (strncmp(key, "ha", 2) == 0);
} }
@ -332,20 +333,53 @@ void _haWebSocketOnConnected(JsonObject& root) {
void _haWebSocketOnAction(uint32_t client_id, const char * action, JsonObject& data) { void _haWebSocketOnAction(uint32_t client_id, const char * action, JsonObject& data) {
if (strcmp(action, "haconfig") == 0) { if (strcmp(action, "haconfig") == 0) {
_ha_send_config.push(client_id);
ws_on_send_callback_list_t callbacks;
#if SENSOR_SUPPORT
callbacks.reserve(magnitudeCount() + relayCount());
#else
callbacks.reserve(relayCount());
#endif // SENSOR_SUPPORT
{
for (unsigned char idx=0; idx<relayCount(); ++idx) {
callbacks.push_back([idx](JsonObject& root) {
_haSwitchYaml(idx, root);
});
}
}
#if SENSOR_SUPPORT
{
for (unsigned char idx=0; idx<magnitudeCount(); ++idx) {
callbacks.push_back([idx](JsonObject& root) {
_haSensorYaml(idx, root);
});
}
}
#endif // SENSOR_SUPPORT
if (callbacks.size()) wsPostSequence(client_id, std::move(callbacks));
} }
} }
#endif
#endif // WEB_SUPPORT
#if TERMINAL_SUPPORT #if TERMINAL_SUPPORT
void _haInitCommands() { void _haInitCommands() {
terminalRegisterCommand(F("HA.CONFIG"), [](Embedis* e) { terminalRegisterCommand(F("HA.CONFIG"), [](Embedis* e) {
_haDumpConfig([](String& data) {
DEBUG_MSG(data.c_str());
});
for (unsigned char idx=0; idx<relayCount(); ++idx) {
DynamicJsonBuffer jsonBuffer(1024);
JsonObject& root = jsonBuffer.createObject();
_haSwitchYaml(idx, root);
DEBUG_MSG(root["haConfig"].as<String>().c_str());
}
#if SENSOR_SUPPORT
for (unsigned char idx=0; idx<magnitudeCount(); ++idx) {
DynamicJsonBuffer jsonBuffer(1024);
JsonObject& root = jsonBuffer.createObject();
_haSensorYaml(idx, root);
DEBUG_MSG(root["haConfig"].as<String>().c_str());
}
#endif // SENSOR_SUPPORT
DEBUG_MSG("\n"); DEBUG_MSG("\n");
terminalOK(); terminalOK();
}); });
@ -374,23 +408,6 @@ void _haInitCommands() {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#if WEB_SUPPORT
void _haLoop() {
if (_ha_send_config.empty()) return;
uint32_t client_id = _ha_send_config.front();
_ha_send_config.pop();
if (!wsConnected(client_id)) return;
// TODO check wsConnected after each "printer" call?
_haDumpConfig([client_id](String& output) {
wsSend(client_id, output.c_str());
yield();
}, true);
}
#endif
void haSetup() { void haSetup() {
_haConfigure(); _haConfigure();
@ -401,7 +418,6 @@ void haSetup() {
.onConnected(_haWebSocketOnConnected) .onConnected(_haWebSocketOnConnected)
.onAction(_haWebSocketOnAction) .onAction(_haWebSocketOnAction)
.onKeyCheck(_haWebSocketOnKeyCheck); .onKeyCheck(_haWebSocketOnKeyCheck);
espurnaRegisterLoop(_haLoop);
#endif #endif
#if TERMINAL_SUPPORT #if TERMINAL_SUPPORT


+ 30
- 4
code/espurna/mqtt.ino View File

@ -74,6 +74,9 @@ String _mqtt_server;
uint16_t _mqtt_port; uint16_t _mqtt_port;
String _mqtt_clientid; String _mqtt_clientid;
String _mqtt_payload_online;
String _mqtt_payload_offline;
std::vector<mqtt_callback_f> _mqtt_callbacks; std::vector<mqtt_callback_f> _mqtt_callbacks;
struct mqtt_message_t { struct mqtt_message_t {
@ -133,7 +136,7 @@ void _mqttSetupAsyncClient(bool secure = false) {
_mqtt.setClientId(_mqtt_clientid.c_str()); _mqtt.setClientId(_mqtt_clientid.c_str());
_mqtt.setKeepAlive(_mqtt_keepalive); _mqtt.setKeepAlive(_mqtt_keepalive);
_mqtt.setCleanSession(false); _mqtt.setCleanSession(false);
_mqtt.setWill(_mqtt_will.c_str(), _mqtt_qos, _mqtt_retain, MQTT_STATUS_OFFLINE);
_mqtt.setWill(_mqtt_will.c_str(), _mqtt_qos, _mqtt_retain, _mqtt_payload_offline.c_str());
if (_mqtt_user.length() && _mqtt_pass.length()) { if (_mqtt_user.length() && _mqtt_pass.length()) {
DEBUG_MSG_P(PSTR("[MQTT] Connecting as user %s\n"), _mqtt_user.c_str()); DEBUG_MSG_P(PSTR("[MQTT] Connecting as user %s\n"), _mqtt_user.c_str());
@ -172,7 +175,7 @@ bool _mqttConnectSyncClient(bool secure = false) {
#if MQTT_LIBRARY == MQTT_LIBRARY_ARDUINOMQTT #if MQTT_LIBRARY == MQTT_LIBRARY_ARDUINOMQTT
_mqtt.begin(_mqtt_server.c_str(), _mqtt_port, (secure ? _mqtt_client_secure->get() : _mqtt_client)); _mqtt.begin(_mqtt_server.c_str(), _mqtt_port, (secure ? _mqtt_client_secure->get() : _mqtt_client));
_mqtt.setWill(_mqtt_will.c_str(), MQTT_STATUS_OFFLINE, _mqtt_qos, _mqtt_retain);
_mqtt.setWill(_mqtt_will.c_str(), _mqtt_payload_offline.c_str(), _mqtt_qos, _mqtt_retain);
result = _mqtt.connect(_mqtt_clientid.c_str(), _mqtt_user.c_str(), _mqtt_pass.c_str()); result = _mqtt.connect(_mqtt_clientid.c_str(), _mqtt_user.c_str(), _mqtt_pass.c_str());
#elif MQTT_LIBRARY == MQTT_LIBRARY_PUBSUBCLIENT #elif MQTT_LIBRARY == MQTT_LIBRARY_PUBSUBCLIENT
_mqtt.setClient(secure ? _mqtt_client_secure->get() : _mqtt_client); _mqtt.setClient(secure ? _mqtt_client_secure->get() : _mqtt_client);
@ -180,9 +183,9 @@ bool _mqttConnectSyncClient(bool secure = false) {
if (_mqtt_user.length() && _mqtt_pass.length()) { if (_mqtt_user.length() && _mqtt_pass.length()) {
DEBUG_MSG_P(PSTR("[MQTT] Connecting as user %s\n"), _mqtt_user.c_str()); DEBUG_MSG_P(PSTR("[MQTT] Connecting as user %s\n"), _mqtt_user.c_str());
result = _mqtt.connect(_mqtt_clientid.c_str(), _mqtt_user.c_str(), _mqtt_pass.c_str(), _mqtt_will.c_str(), _mqtt_qos, _mqtt_retain, MQTT_STATUS_OFFLINE);
result = _mqtt.connect(_mqtt_clientid.c_str(), _mqtt_user.c_str(), _mqtt_pass.c_str(), _mqtt_will.c_str(), _mqtt_qos, _mqtt_retain, _mqtt_payload_offline.c_str());
} else { } else {
result = _mqtt.connect(_mqtt_clientid.c_str(), _mqtt_will.c_str(), _mqtt_qos, _mqtt_retain, MQTT_STATUS_OFFLINE);
result = _mqtt.connect(_mqtt_clientid.c_str(), _mqtt_will.c_str(), _mqtt_qos, _mqtt_retain, _mqtt_payload_offline.c_str());
} }
#endif #endif
@ -357,6 +360,13 @@ void _mqttConfigure() {
_mqttApplyTopic(_mqtt_topic_json, MQTT_TOPIC_JSON); _mqttApplyTopic(_mqtt_topic_json, MQTT_TOPIC_JSON);
} }
// Custom payload strings
settingsProcessConfig({
{_mqtt_payload_online, "mqttPayloadOnline", MQTT_STATUS_ONLINE},
{_mqtt_payload_offline, "mqttPayloadOffline", MQTT_STATUS_OFFLINE}
});
// Reset reconnect delay to reconnect sooner
_mqtt_reconnect_delay = MQTT_RECONNECT_DELAY_MIN; _mqtt_reconnect_delay = MQTT_RECONNECT_DELAY_MIN;
} }
@ -868,6 +878,22 @@ void mqttSetBrokerIfNone(IPAddress ip, unsigned int port) {
if (getSetting("mqttServer", MQTT_SERVER).length() == 0) mqttSetBroker(ip, port); if (getSetting("mqttServer", MQTT_SERVER).length() == 0) mqttSetBroker(ip, port);
} }
const String& mqttPayloadOnline() {
return _mqtt_payload_online;
}
const String& mqttPayloadOffline() {
return _mqtt_payload_offline;
}
const char* mqttPayloadStatus(bool status) {
return status ? _mqtt_payload_online.c_str() : _mqtt_payload_offline.c_str();
}
void mqttSendStatus() {
mqttSend(MQTT_TOPIC_STATUS, _mqtt_payload_online.c_str(), true);
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Initialization // Initialization
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------


+ 29
- 3
code/espurna/relay.ino View File

@ -44,6 +44,13 @@ std::vector<relay_t> _relays;
bool _relayRecursive = false; bool _relayRecursive = false;
Ticker _relaySaveTicker; Ticker _relaySaveTicker;
#if MQTT_SUPPORT
String _relay_mqtt_payload_on;
String _relay_mqtt_payload_off;
#endif // MQTT_SUPPORT
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// RELAY PROVIDERS // RELAY PROVIDERS
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -616,6 +623,13 @@ void _relayConfigure() {
digitalWrite(_relays[i].pin, HIGH); digitalWrite(_relays[i].pin, HIGH);
} }
} }
#if MQTT_SUPPORT
settingsProcessConfig({
{_relay_mqtt_payload_on, "relayPayloadON", RELAY_MQTT_ON},
{_relay_mqtt_payload_off, "relayPayloadOFF", RELAY_MQTT_OFF}
});
#endif // MQTT_SUPPORT
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -857,6 +871,18 @@ void relaySetupAPI() {
#if MQTT_SUPPORT #if MQTT_SUPPORT
const String& relayPayloadOn() {
return _relay_mqtt_payload_on;
}
const String& relayPayloadOff() {
return _relay_mqtt_payload_off;
}
const char* relayPayload(bool status) {
return status ? _relay_mqtt_payload_on.c_str() : _relay_mqtt_payload_off.c_str();
}
void _relayMQTTGroup(unsigned char id) { void _relayMQTTGroup(unsigned char id) {
String topic = getSetting("mqttGroup", id, ""); String topic = getSetting("mqttGroup", id, "");
if (!topic.length()) return; if (!topic.length()) return;
@ -866,7 +892,7 @@ void _relayMQTTGroup(unsigned char id) {
bool status = relayStatus(id); bool status = relayStatus(id);
if (mode == RELAY_GROUP_SYNC_INVERSE) status = !status; if (mode == RELAY_GROUP_SYNC_INVERSE) status = !status;
mqttSendRaw(topic.c_str(), status ? RELAY_MQTT_ON : RELAY_MQTT_OFF);
mqttSendRaw(topic.c_str(), relayPayload(status));
} }
void relayMQTT(unsigned char id) { void relayMQTT(unsigned char id) {
@ -876,7 +902,7 @@ void relayMQTT(unsigned char id) {
// Send state topic // Send state topic
if (_relays[id].report) { if (_relays[id].report) {
_relays[id].report = false; _relays[id].report = false;
mqttSend(MQTT_TOPIC_RELAY, id, _relays[id].current_status ? RELAY_MQTT_ON : RELAY_MQTT_OFF);
mqttSend(MQTT_TOPIC_RELAY, id, relayPayload(_relays[id].current_status));
} }
// Check group topic // Check group topic
@ -896,7 +922,7 @@ void relayMQTT(unsigned char id) {
void relayMQTT() { void relayMQTT() {
for (unsigned int id=0; id < _relays.size(); id++) { for (unsigned int id=0; id < _relays.size(); id++) {
mqttSend(MQTT_TOPIC_RELAY, id, _relays[id].current_status ? RELAY_MQTT_ON : RELAY_MQTT_OFF);
mqttSend(MQTT_TOPIC_RELAY, id, relayPayload(_relays[id].current_status));
} }
} }


+ 11
- 0
code/espurna/settings.ino View File

@ -245,6 +245,17 @@ void settingsGetJson(JsonObject& root) {
} }
void settingsProcessConfig(const settings_cfg_list_t& config, settings_filter_t filter) {
for (auto& entry : config) {
String value = getSetting(entry.key, entry.default_value);
if (filter) {
value = filter(value);
}
if (value.equals(entry.setting)) continue;
entry.setting = std::move(value);
}
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Initialization // Initialization
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------


+ 2
- 2
code/espurna/utils.ino View File

@ -272,7 +272,7 @@ void heartbeat() {
mqttSend(MQTT_TOPIC_VCC, String(ESP.getVcc()).c_str()); mqttSend(MQTT_TOPIC_VCC, String(ESP.getVcc()).c_str());
if (hb_cfg & Heartbeat::Status) if (hb_cfg & Heartbeat::Status)
mqttSend(MQTT_TOPIC_STATUS, MQTT_STATUS_ONLINE, true);
mqttSendStatus();
if (hb_cfg & Heartbeat::Loadavg) if (hb_cfg & Heartbeat::Loadavg)
mqttSend(MQTT_TOPIC_LOADAVG, String(systemLoadAverage()).c_str()); mqttSend(MQTT_TOPIC_LOADAVG, String(systemLoadAverage()).c_str());
@ -291,7 +291,7 @@ void heartbeat() {
#endif #endif
} else if (!serial && _heartbeat_mode == HEARTBEAT_REPEAT_STATUS) { } else if (!serial && _heartbeat_mode == HEARTBEAT_REPEAT_STATUS) {
mqttSend(MQTT_TOPIC_STATUS, MQTT_STATUS_ONLINE, true);
mqttSendStatus();
} }
#endif #endif


+ 12
- 0
code/espurna/ws.ino View File

@ -91,6 +91,14 @@ struct ws_data_t {
counter(0, 1) counter(0, 1)
{} {}
ws_data_t(const uint32_t client_id, ws_on_send_callback_list_t&& callbacks, mode_t mode = SEQUENCE) :
storage(new ws_on_send_callback_list_t(std::move(callbacks))),
client_id(client_id),
mode(mode),
callbacks(*storage.get()),
counter(0, (storage.get())->size())
{}
ws_data_t(const uint32_t client_id, const ws_on_send_callback_list_t& callbacks, mode_t mode = SEQUENCE) : ws_data_t(const uint32_t client_id, const ws_on_send_callback_list_t& callbacks, mode_t mode = SEQUENCE) :
client_id(client_id), client_id(client_id),
mode(mode), mode(mode),
@ -764,6 +772,10 @@ void wsPostSequence(uint32_t client_id, const ws_on_send_callback_list_t& cbs) {
_ws_client_data.emplace(client_id, cbs, ws_data_t::SEQUENCE); _ws_client_data.emplace(client_id, cbs, ws_data_t::SEQUENCE);
} }
void wsPostSequence(uint32_t client_id, ws_on_send_callback_list_t&& cbs) {
_ws_client_data.emplace(client_id, std::forward<ws_on_send_callback_list_t>(cbs), ws_data_t::SEQUENCE);
}
void wsPostSequence(const ws_on_send_callback_list_t& cbs) { void wsPostSequence(const ws_on_send_callback_list_t& cbs) {
_ws_client_data.emplace(0, cbs, ws_data_t::SEQUENCE); _ws_client_data.emplace(0, cbs, ws_data_t::SEQUENCE);
} }


Loading…
Cancel
Save