Browse Source

relay/mqtt: handle custom relay status payloads (#1889)

- customize relay TOGGLE payload
- match payload string when receiving mqtt status message
- reference enum values instead of raw integers, spell out intended status
- remove dead code

amend #1885, capitalize `relayPayload...` suffix instead of using uppercase
add `relayPayloadToggle`
master
Max Prokhorov 5 years ago
committed by GitHub
parent
commit
beff73ef8b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 123 additions and 90 deletions
  1. +7
    -3
      code/espurna/config/general.h
  2. +11
    -2
      code/espurna/config/prototypes.h
  3. +2
    -2
      code/espurna/homeassistant.ino
  4. +3
    -3
      code/espurna/led.ino
  5. +100
    -80
      code/espurna/relay.ino

+ 7
- 3
code/espurna/config/general.h View File

@ -404,13 +404,17 @@
#define RELAY_REPORT_STATUS 1 #define RELAY_REPORT_STATUS 1
#endif #endif
// Configure the MQTT payload for ON/OFF
// Configure the MQTT payload for ON, OFF and TOGGLE
#ifndef RELAY_MQTT_OFF
#define RELAY_MQTT_OFF "0"
#endif
#ifndef RELAY_MQTT_ON #ifndef RELAY_MQTT_ON
#define RELAY_MQTT_ON "1" #define RELAY_MQTT_ON "1"
#endif #endif
#ifndef RELAY_MQTT_OFF
#define RELAY_MQTT_OFF "0"
#ifndef RELAY_MQTT_TOGGLE
#define RELAY_MQTT_TOGGLE "2"
#endif #endif
// TODO Only single EEPROM address is used to store state, which is 1 byte // TODO Only single EEPROM address is used to store state, which is 1 byte


+ 11
- 2
code/espurna/config/prototypes.h View File

@ -254,6 +254,15 @@ typedef struct {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
#include <bitset> #include <bitset>
enum class RelayStatus : unsigned char {
OFF = 0,
ON = 1,
TOGGLE = 2,
UNKNOWN = 0xFF
};
RelayStatus relayParsePayload(const char * payload);
bool relayStatus(unsigned char id, bool status, bool report, bool group_report); bool relayStatus(unsigned char id, bool status, bool report, bool group_report);
bool relayStatus(unsigned char id, bool status); bool relayStatus(unsigned char id, bool status);
bool relayStatus(unsigned char id); bool relayStatus(unsigned char id);
@ -262,11 +271,11 @@ void relayToggle(unsigned char id, bool report, bool group_report);
void relayToggle(unsigned char id); void relayToggle(unsigned char id);
unsigned char relayCount(); unsigned char relayCount();
unsigned char relayParsePayload(const char * payload);
const String& relayPayloadOn(); const String& relayPayloadOn();
const String& relayPayloadOff(); const String& relayPayloadOff();
const char* relayPayload(bool status);
const String& relayPayloadToggle();
const char* relayPayload(RelayStatus status);
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Settings // Settings


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

@ -144,8 +144,8 @@ void _haSendSwitch(unsigned char i, JsonObject& config) {
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"] = relayPayload(true);
config["payload_off"] = relayPayload(false);
config["payload_on"] = relayPayload(RelayStatus::ON);
config["payload_off"] = relayPayload(RelayStatus::OFF);
config["availability_topic"] = mqttTopic(MQTT_TOPIC_STATUS, false); config["availability_topic"] = mqttTopic(MQTT_TOPIC_STATUS, false);
config["payload_available"] = mqttPayloadStatus(true); config["payload_available"] = mqttPayloadStatus(true);
config["payload_not_available"] = mqttPayloadStatus(false); config["payload_not_available"] = mqttPayloadStatus(false);


+ 3
- 3
code/espurna/led.ino View File

@ -130,13 +130,13 @@ void _ledMQTTCallback(unsigned int type, const char * topic, const char * payloa
if (_ledMode(ledID) != LED_MODE_MQTT) return; if (_ledMode(ledID) != LED_MODE_MQTT) return;
// get value // get value
unsigned char value = relayParsePayload(payload);
const auto value = relayParsePayload(payload);
// Action to perform // Action to perform
if (value == 2) {
if (value == RelayStatus::TOGGLE) {
_ledToggle(ledID); _ledToggle(ledID);
} else { } else {
_ledStatus(ledID, value == 1);
_ledStatus(ledID, (value == RelayStatus::ON));
} }
} }


+ 100
- 80
code/espurna/relay.ino View File

@ -48,9 +48,40 @@ Ticker _relaySaveTicker;
String _relay_mqtt_payload_on; String _relay_mqtt_payload_on;
String _relay_mqtt_payload_off; String _relay_mqtt_payload_off;
String _relay_mqtt_payload_toggle;
#endif // MQTT_SUPPORT #endif // MQTT_SUPPORT
// -----------------------------------------------------------------------------
// UTILITY
// -----------------------------------------------------------------------------
bool _relayHandlePayload(unsigned char relayID, const char* payload) {
auto value = relayParsePayload(payload);
if (value == RelayStatus::UNKNOWN) return false;
if (value == RelayStatus::OFF) {
relayStatus(relayID, false);
} else if (value == RelayStatus::ON) {
relayStatus(relayID, true);
} else if (value == RelayStatus::TOGGLE) {
relayToggle(relayID);
}
return true;
}
RelayStatus _relayStatusInvert(RelayStatus status) {
return (status == RelayStatus::ON) ? RelayStatus::OFF : status;
}
RelayStatus _relayStatusTyped(unsigned char id) {
if (id >= _relays.size()) return RelayStatus::OFF;
const bool status = _relays[id].current_status;
return (status) ? RelayStatus::ON : RelayStatus::OFF;
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// RELAY PROVIDERS // RELAY PROVIDERS
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -377,10 +408,10 @@ bool relayStatus(unsigned char id, bool status) {
bool relayStatus(unsigned char id) { bool relayStatus(unsigned char id) {
// Check relay ID
// Check that relay ID is valid
if (id >= _relays.size()) return false; if (id >= _relays.size()) return false;
// Get status from storage
// Get status directly from storage
return _relays[id].current_status; return _relays[id].current_status;
} }
@ -484,37 +515,40 @@ unsigned char relayCount() {
return _relays.size(); return _relays.size();
} }
unsigned char relayParsePayload(const char * payload) {
RelayStatus relayParsePayload(const char * payload) {
// Payload could be "OFF", "ON", "TOGGLE"
// or its number equivalents: 0, 1 or 2
// Don't parse empty strings
const auto len = strlen(payload);
if (!len) return RelayStatus::UNKNOWN;
if (payload[0] == '0') return 0;
if (payload[0] == '1') return 1;
if (payload[0] == '2') return 2;
// Check most commonly used payloads
if (len == 1) {
if (payload[0] == '0') return RelayStatus::OFF;
if (payload[0] == '1') return RelayStatus::ON;
if (payload[0] == '2') return RelayStatus::TOGGLE;
return RelayStatus::UNKNOWN;
}
// trim payload
char * p = ltrim((char *)payload);
// If possible, compare to locally configured payload strings
#if MQTT_SUPPORT
if (_relay_mqtt_payload_off.equals(payload)) return RelayStatus::OFF;
if (_relay_mqtt_payload_on.equals(payload)) return RelayStatus::ON;
if (_relay_mqtt_payload_toggle.equals(payload)) return RelayStatus::TOGGLE;
#endif // MQTT_SUPPORT
// to lower
unsigned int l = strlen(p);
if (l>6) l=6;
for (unsigned char i=0; i<l; i++) {
p[i] = tolower(p[i]);
}
// Finally, check for "OFF", "ON", "TOGGLE" (both lower and upper cases)
String temp(payload);
temp.trim();
unsigned int value = 0xFF;
if (strcmp(p, "off") == 0) {
value = 0;
} else if (strcmp(p, "on") == 0) {
value = 1;
} else if (strcmp(p, "toggle") == 0) {
value = 2;
} else if (strcmp(p, "query") == 0) {
value = 3;
if (temp.equalsIgnoreCase("off")) {
return RelayStatus::OFF;
} else if (temp.equalsIgnoreCase("on")) {
return RelayStatus::ON;
} else if (temp.equalsIgnoreCase("toggle")) {
return RelayStatus::TOGGLE;
} }
return value;
return RelayStatus::UNKNOWN;
} }
@ -626,8 +660,9 @@ void _relayConfigure() {
#if MQTT_SUPPORT #if MQTT_SUPPORT
settingsProcessConfig({ settingsProcessConfig({
{_relay_mqtt_payload_on, "relayPayloadON", RELAY_MQTT_ON},
{_relay_mqtt_payload_off, "relayPayloadOFF", RELAY_MQTT_OFF}
{_relay_mqtt_payload_on, "relayPayloadOn", RELAY_MQTT_ON},
{_relay_mqtt_payload_off, "relayPayloadOff", RELAY_MQTT_OFF},
{_relay_mqtt_payload_toggle, "relayPayloadToggle", RELAY_MQTT_OFF},
}); });
#endif // MQTT_SUPPORT #endif // MQTT_SUPPORT
} }
@ -747,31 +782,13 @@ void _relayWebSocketOnAction(uint32_t client_id, const char * action, JsonObject
if (data.containsKey("status")) { if (data.containsKey("status")) {
unsigned char value = relayParsePayload(data["status"]);
if (value == 3) {
wsPost(_relayWebSocketUpdate);
} else if (value < 3) {
unsigned int relayID = 0;
if (data.containsKey("id")) {
String value = data["id"];
relayID = value.toInt();
}
// Action to perform
if (value == 0) {
relayStatus(relayID, false);
} else if (value == 1) {
relayStatus(relayID, true);
} else if (value == 2) {
relayToggle(relayID);
}
unsigned int relayID = 0;
if (data.containsKey("id") && data.is<int>("id")) {
relayID = data["id"];
} }
_relayHandlePayload(relayID, data["status"]);
} }
} }
@ -807,21 +824,11 @@ void relaySetupAPI() {
}, },
[relayID](const char * payload) { [relayID](const char * payload) {
unsigned char value = relayParsePayload(payload);
if (value == 0xFF) {
if (_relayHandlePayload(relayID, payload)) {
DEBUG_MSG_P(PSTR("[RELAY] Wrong payload (%s)\n"), payload); DEBUG_MSG_P(PSTR("[RELAY] Wrong payload (%s)\n"), payload);
return; return;
} }
if (value == 0) {
relayStatus(relayID, false);
} else if (value == 1) {
relayStatus(relayID, true);
} else if (value == 2) {
relayToggle(relayID);
}
} }
); );
@ -879,8 +886,21 @@ const String& relayPayloadOff() {
return _relay_mqtt_payload_off; return _relay_mqtt_payload_off;
} }
const char* relayPayload(bool status) {
return status ? _relay_mqtt_payload_on.c_str() : _relay_mqtt_payload_off.c_str();
const String& relayPayloadToggle() {
return _relay_mqtt_payload_toggle;
}
const char* relayPayload(RelayStatus status) {
if (status == RelayStatus::ON) {
return _relay_mqtt_payload_off.c_str();
} else if (status == RelayStatus::OFF) {
return _relay_mqtt_payload_on.c_str();
} else if (status == RelayStatus::TOGGLE) {
return _relay_mqtt_payload_toggle.c_str();
}
return "";
} }
void _relayMQTTGroup(unsigned char id) { void _relayMQTTGroup(unsigned char id) {
@ -890,8 +910,8 @@ void _relayMQTTGroup(unsigned char id) {
unsigned char mode = getSetting("mqttGroupSync", id, RELAY_GROUP_SYNC_NORMAL).toInt(); unsigned char mode = getSetting("mqttGroupSync", id, RELAY_GROUP_SYNC_NORMAL).toInt();
if (mode == RELAY_GROUP_SYNC_RECEIVEONLY) return; if (mode == RELAY_GROUP_SYNC_RECEIVEONLY) return;
bool status = relayStatus(id);
if (mode == RELAY_GROUP_SYNC_INVERSE) status = !status;
auto status = _relayStatusTyped(id);
if (mode == RELAY_GROUP_SYNC_INVERSE) status = _relayStatusInvert(status);
mqttSendRaw(topic.c_str(), relayPayload(status)); mqttSendRaw(topic.c_str(), relayPayload(status));
} }
@ -902,7 +922,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, relayPayload(_relays[id].current_status));
mqttSend(MQTT_TOPIC_RELAY, id, relayPayload(_relayStatusTyped(_relays[id].current_status)));
} }
// Check group topic // Check group topic
@ -922,19 +942,19 @@ 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, relayPayload(_relays[id].current_status));
mqttSend(MQTT_TOPIC_RELAY, id, relayPayload(_relayStatusTyped(_relays[id].current_status)));
} }
} }
void relayStatusWrap(unsigned char id, unsigned char value, bool is_group_topic) {
void relayStatusWrap(unsigned char id, RelayStatus value, bool is_group_topic) {
switch (value) { switch (value) {
case 0:
case RelayStatus::OFF:
relayStatus(id, false, mqttForward(), !is_group_topic); relayStatus(id, false, mqttForward(), !is_group_topic);
break; break;
case 1:
case RelayStatus::ON:
relayStatus(id, true, mqttForward(), !is_group_topic); relayStatus(id, true, mqttForward(), !is_group_topic);
break; break;
case 2:
case RelayStatus::TOGGLE:
relayToggle(id, true, true); relayToggle(id, true, true);
break; break;
default: default:
@ -1015,8 +1035,8 @@ void relayMQTTCallback(unsigned int type, const char * topic, const char * paylo
} }
// Get value // Get value
unsigned char value = relayParsePayload(payload);
if (value == 0xFF) return;
auto value = relayParsePayload(payload);
if (value == RelayStatus::UNKNOWN) return;
relayStatusWrap(id, value, false); relayStatusWrap(id, value, false);
@ -1031,12 +1051,12 @@ void relayMQTTCallback(unsigned int type, const char * topic, const char * paylo
if ((t.length() > 0) && t.equals(topic)) { if ((t.length() > 0) && t.equals(topic)) {
unsigned char value = relayParsePayload(payload);
if (value == 0xFF) return;
auto value = relayParsePayload(payload);
if (value == RelayStatus::UNKNOWN) return;
if (value < 2) {
if ((value == RelayStatus::ON) || (value == RelayStatus::OFF)) {
if (getSetting("mqttGroupSync", i, RELAY_GROUP_SYNC_NORMAL).toInt() == RELAY_GROUP_SYNC_INVERSE) { if (getSetting("mqttGroupSync", i, RELAY_GROUP_SYNC_NORMAL).toInt() == RELAY_GROUP_SYNC_INVERSE) {
value = 1 - value;
value = _relayStatusInvert(value);
} }
} }
@ -1060,10 +1080,10 @@ void relayMQTTCallback(unsigned int type, const char * topic, const char * paylo
int reaction = getSetting("relayOnDisc", i, 0).toInt(); int reaction = getSetting("relayOnDisc", i, 0).toInt();
if (1 == reaction) { // switch relay OFF if (1 == reaction) { // switch relay OFF
DEBUG_MSG_P(PSTR("[RELAY] Reset relay (%d) due to MQTT disconnection\n"), i); DEBUG_MSG_P(PSTR("[RELAY] Reset relay (%d) due to MQTT disconnection\n"), i);
relayStatusWrap(i, false, false);
relayStatusWrap(i, RelayStatus::OFF, false);
} else if(2 == reaction) { // switch relay ON } else if(2 == reaction) { // switch relay ON
DEBUG_MSG_P(PSTR("[RELAY] Set relay (%d) due to MQTT disconnection\n"), i); DEBUG_MSG_P(PSTR("[RELAY] Set relay (%d) due to MQTT disconnection\n"), i);
relayStatusWrap(i, true, false);
relayStatusWrap(i, RelayStatus::ON, false);
} }
} }


Loading…
Cancel
Save