Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MQTT: handle custom relay status payloads #1889

Merged
merged 3 commits into from
Sep 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions code/espurna/config/general.h
Original file line number Diff line number Diff line change
Expand Up @@ -404,13 +404,17 @@
#define RELAY_REPORT_STATUS 1
#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
#define RELAY_MQTT_ON "1"
#endif

#ifndef RELAY_MQTT_OFF
#define RELAY_MQTT_OFF "0"
#ifndef RELAY_MQTT_TOGGLE
#define RELAY_MQTT_TOGGLE "2"
#endif

// TODO Only single EEPROM address is used to store state, which is 1 byte
Expand Down
13 changes: 11 additions & 2 deletions code/espurna/config/prototypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,15 @@ typedef struct {
// -----------------------------------------------------------------------------
#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 relayStatus(unsigned char id);
Expand All @@ -262,11 +271,11 @@ 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);
const String& relayPayloadToggle();
const char* relayPayload(RelayStatus status);

// -----------------------------------------------------------------------------
// Settings
Expand Down
4 changes: 2 additions & 2 deletions code/espurna/homeassistant.ino
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,8 @@ void _haSendSwitch(unsigned char i, JsonObject& config) {
if (relayCount()) {
config["state_topic"] = mqttTopic(MQTT_TOPIC_RELAY, i, false);
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["payload_available"] = mqttPayloadStatus(true);
config["payload_not_available"] = mqttPayloadStatus(false);
Expand Down
6 changes: 3 additions & 3 deletions code/espurna/led.ino
Original file line number Diff line number Diff line change
Expand Up @@ -130,13 +130,13 @@ void _ledMQTTCallback(unsigned int type, const char * topic, const char * payloa
if (_ledMode(ledID) != LED_MODE_MQTT) return;

// get value
unsigned char value = relayParsePayload(payload);
const auto value = relayParsePayload(payload);

// Action to perform
if (value == 2) {
if (value == RelayStatus::TOGGLE) {
_ledToggle(ledID);
} else {
_ledStatus(ledID, value == 1);
_ledStatus(ledID, (value == RelayStatus::ON));
}

}
Expand Down
180 changes: 100 additions & 80 deletions code/espurna/relay.ino
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,40 @@ Ticker _relaySaveTicker;

String _relay_mqtt_payload_on;
String _relay_mqtt_payload_off;
String _relay_mqtt_payload_toggle;

#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
// -----------------------------------------------------------------------------
Expand Down Expand Up @@ -377,10 +408,10 @@ bool relayStatus(unsigned char id, bool status) {

bool relayStatus(unsigned char id) {

// Check relay ID
// Check that relay ID is valid
if (id >= _relays.size()) return false;

// Get status from storage
// Get status directly from storage
return _relays[id].current_status;

}
Expand Down Expand Up @@ -484,37 +515,40 @@ unsigned char relayCount() {
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;

}

Expand Down Expand Up @@ -626,8 +660,9 @@ void _relayConfigure() {

#if MQTT_SUPPORT
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
}
Expand Down Expand Up @@ -747,31 +782,13 @@ void _relayWebSocketOnAction(uint32_t client_id, const char * action, JsonObject

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"]);

}

}
Expand Down Expand Up @@ -807,21 +824,11 @@ void relaySetupAPI() {
},
[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);
return;
}

if (value == 0) {
relayStatus(relayID, false);
} else if (value == 1) {
relayStatus(relayID, true);
} else if (value == 2) {
relayToggle(relayID);
}

}
);

Expand Down Expand Up @@ -879,8 +886,21 @@ 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();
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) {
Expand All @@ -890,8 +910,8 @@ void _relayMQTTGroup(unsigned char id) {
unsigned char mode = getSetting("mqttGroupSync", id, RELAY_GROUP_SYNC_NORMAL).toInt();
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));
}

Expand All @@ -902,7 +922,7 @@ void relayMQTT(unsigned char id) {
// Send state topic
if (_relays[id].report) {
_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
Expand All @@ -922,19 +942,19 @@ void relayMQTT(unsigned char id) {

void relayMQTT() {
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) {
case 0:
case RelayStatus::OFF:
relayStatus(id, false, mqttForward(), !is_group_topic);
break;
case 1:
case RelayStatus::ON:
relayStatus(id, true, mqttForward(), !is_group_topic);
break;
case 2:
case RelayStatus::TOGGLE:
relayToggle(id, true, true);
break;
default:
Expand Down Expand Up @@ -1015,8 +1035,8 @@ void relayMQTTCallback(unsigned int type, const char * topic, const char * paylo
}

// Get value
unsigned char value = relayParsePayload(payload);
if (value == 0xFF) return;
auto value = relayParsePayload(payload);
if (value == RelayStatus::UNKNOWN) return;

relayStatusWrap(id, value, false);

Expand All @@ -1031,12 +1051,12 @@ void relayMQTTCallback(unsigned int type, const char * topic, const char * paylo

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) {
value = 1 - value;
value = _relayStatusInvert(value);
}
}

Expand All @@ -1060,10 +1080,10 @@ void relayMQTTCallback(unsigned int type, const char * topic, const char * paylo
int reaction = getSetting("relayOnDisc", i, 0).toInt();
if (1 == reaction) { // switch relay OFF
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
DEBUG_MSG_P(PSTR("[RELAY] Set relay (%d) due to MQTT disconnection\n"), i);
relayStatusWrap(i, true, false);
relayStatusWrap(i, RelayStatus::ON, false);
}
}

Expand Down