diff --git a/usermods/multi_relay/readme.md b/usermods/multi_relay/readme.md index ebf2056a..267bb54a 100644 --- a/usermods/multi_relay/readme.md +++ b/usermods/multi_relay/readme.md @@ -36,7 +36,7 @@ wled/deviceMAC/relay/0 on|off 1. Register the usermod by adding `#include "../usermods/multi_relay/usermod_multi_relay.h"` at the top and `usermods.add(new MultiRelay());` at the bottom of `usermods_list.cpp`. or -2. Use `#define USERMOD_MULTI_RELAY` in wled.h or `-D USERMOD_MULTI_RELAY`in your platformio.ini +2. Use `#define USERMOD_MULTI_RELAY` in wled.h or `-D USERMOD_MULTI_RELAY` in your platformio.ini You can override the default maximum number (4) of relays by defining MULTI_RELAY_MAX_RELAYS. diff --git a/usermods/multi_relay/usermod_multi_relay.h b/usermods/multi_relay/usermod_multi_relay.h index 9a92d4e2..23dcc1e2 100644 --- a/usermods/multi_relay/usermod_multi_relay.h +++ b/usermods/multi_relay/usermod_multi_relay.h @@ -45,6 +45,11 @@ class MultiRelay : public Usermod { // status of initialisation bool initDone = false; + bool HAautodiscovery = false; + + uint16_t periodicBroadcastSec = 60; + unsigned long lastBroadcast = 0; + // strings to reduce flash memory usage (used more than twice) static const char _name[]; static const char _enabled[]; @@ -53,14 +58,15 @@ class MultiRelay : public Usermod { static const char _activeHigh[]; static const char _external[]; static const char _button[]; + static const char _broadcast[]; + static const char _HAautodiscovery[]; - - void publishMqtt(const char* state, int relay) { + void publishMqtt(int relay) { //Check if MQTT Connected, otherwise it will crash the 8266 if (WLED_MQTT_CONNECTED){ char subuf[64]; sprintf_P(subuf, PSTR("%s/relay/%d"), mqttDeviceTopic, relay); - mqtt->publish(subuf, 0, false, state); + mqtt->publish(subuf, 0, false, _relay[relay].state ? "on" : "off"); } } @@ -68,15 +74,19 @@ class MultiRelay : public Usermod { * switch off the strip if the delay has elapsed */ void handleOffTimer() { + unsigned long now = millis(); bool activeRelays = false; for (uint8_t i=0; i 0 && millis() - _switchTimerStart > (_relay[i].delay*1000)) { + if (_relay[i].active && _switchTimerStart > 0 && now - _switchTimerStart > (_relay[i].delay*1000)) { if (!_relay[i].external) toggleRelay(i); _relay[i].active = false; + } else if (periodicBroadcastSec && now - lastBroadcast > (periodicBroadcastSec*1000)) { + if (_relay[i].pin>=0) publishMqtt(i); } activeRelays = activeRelays || _relay[i].active; } if (!activeRelays) _switchTimerStart = 0; + if (periodicBroadcastSec && now - lastBroadcast > (periodicBroadcastSec*1000)) lastBroadcast = now; } /** @@ -105,7 +115,7 @@ class MultiRelay : public Usermod { for (int i=0; ivalue(), ',', i); if (value==-1) { - error = F("There must be as much arugments as relays"); + error = F("There must be as many arguments as relays"); } else { // Switch if (_relay[i].external) switchRelay(i, (bool)value); @@ -118,7 +128,7 @@ class MultiRelay : public Usermod { for (int i=0;ivalue(), ',', i); if (value==-1) { - error = F("There must be as mutch arugments as relays"); + error = F("There must be as many arguments as relays"); } else { // Toggle if (value && _relay[i].external) toggleRelay(i); @@ -199,7 +209,7 @@ class MultiRelay : public Usermod { _relay[relay].state = mode; pinMode(_relay[relay].pin, OUTPUT); digitalWrite(_relay[relay].pin, mode ? !_relay[relay].mode : _relay[relay].mode); - publishMqtt(mode ? "on" : "off", relay); + publishMqtt(relay); } /** @@ -252,6 +262,50 @@ class MultiRelay : public Usermod { strcpy(subuf, mqttDeviceTopic); strcat_P(subuf, PSTR("/relay/#")); mqtt->subscribe(subuf, 0); + if (HAautodiscovery) publishHomeAssistantAutodiscovery(); + for (uint8_t i=0; i= 0 && _relay[i].external) { + StaticJsonDocument<1024> json; + sprintf_P(buf, PSTR("%s Switch %d"), serverDescription, i); //max length: 33 + 8 + 3 = 44 + json[F("name")] = buf; + + sprintf_P(buf, PSTR("%s/relay/%d"), mqttDeviceTopic, i); //max length: 33 + 7 + 3 = 43 + json["~"] = buf; + strcat_P(buf, PSTR("/command")); + mqtt->subscribe(buf, 0); + + json[F("stat_t")] = "~"; + json[F("cmd_t")] = F("~/command"); + json[F("pl_off")] = F("off"); + json[F("pl_on")] = F("on"); + json[F("uniq_id")] = uid; + + strcpy(buf, mqttDeviceTopic); //max length: 33 + 7 = 40 + strcat_P(buf, PSTR("/status")); + json[F("avty_t")] = buf; + json[F("pl_avail")] = F("online"); + json[F("pl_not_avail")] = F("offline"); + //TODO: dev + payload_size = serializeJson(json, json_str); + } else { + //Unpublish disabled or internal relays + json_str[0] = 0; + payload_size = 0; + } + sprintf_P(buf, PSTR("homeassistant/switch/%s/config"), uid); + mqtt->publish(buf, 0, true, json_str, payload_size); } } @@ -266,7 +320,7 @@ class MultiRelay : public Usermod { if (!pinManager.allocatePin(_relay[i].pin,true, PinOwner::UM_MultiRelay)) { _relay[i].pin = -1; // allocation failed } else { - if (!_relay[i].external) _relay[i].state = offMode; + if (!_relay[i].external) _relay[i].state = !offMode; switchRelay(i, _relay[i].state); _relay[i].active = false; } @@ -313,13 +367,18 @@ class MultiRelay : public Usermod { */ bool handleButton(uint8_t b) { yield(); - if (buttonType[b] == BTN_TYPE_NONE || buttonType[b] == BTN_TYPE_RESERVED || buttonType[b] == BTN_TYPE_PIR_SENSOR || buttonType[b] == BTN_TYPE_ANALOG || buttonType[b] == BTN_TYPE_ANALOG_INVERTED) { + if (!enabled + || buttonType[b] == BTN_TYPE_NONE + || buttonType[b] == BTN_TYPE_RESERVED + || buttonType[b] == BTN_TYPE_PIR_SENSOR + || buttonType[b] == BTN_TYPE_ANALOG + || buttonType[b] == BTN_TYPE_ANALOG_INVERTED) { return false; } bool handled = false; for (uint8_t i=0; i 600) { //long press + //longPressAction(b); //not exposed + //handled = false; //use if you want to pass to default behaviour buttonLongPressed[b] = true; } @@ -371,7 +432,8 @@ class MultiRelay : public Usermod { if (!buttonLongPressed[b]) { //short press // if this is second release within 350ms it is a double press (buttonWaitTime!=0) if (doublePress) { - //doublePressAction(b); + //doublePressAction(b); //not exposed + //handled = false; //use if you want to pass to default behaviour } else { buttonWaitTime[b] = now; } @@ -379,9 +441,10 @@ class MultiRelay : public Usermod { buttonPressedBefore[b] = false; buttonLongPressed[b] = false; } - // if 450ms elapsed since last press/release it is a short press + // if 350ms elapsed since last press/release it is a short press if (buttonWaitTime[b] && now - buttonWaitTime[b] > 350 && !buttonPressedBefore[b]) { buttonWaitTime[b] = 0; + //shortPressAction(b); //not exposed for (uint8_t i=0; i=0 && _relay[i].button == b) { toggleRelay(i); @@ -477,6 +540,7 @@ class MultiRelay : public Usermod { JsonObject top = root.createNestedObject(FPSTR(_name)); top[FPSTR(_enabled)] = enabled; + top[FPSTR(_broadcast)] = periodicBroadcastSec; for (uint8_t i=0; i=0 && pinManager.allocatePin(_relay[i].pin, true, PinOwner::UM_MultiRelay)) { if (!_relay[i].external) { - switchRelay(i, offMode); + _relay[i].state = !offMode; + switchRelay(i, _relay[i].state); + _oldMode = offMode; } } else { _relay[i].pin = -1; @@ -549,7 +619,7 @@ class MultiRelay : public Usermod { DEBUG_PRINTLN(F(" config (re)loaded.")); } // use "return !top["newestParameter"].isNull();" when updating Usermod with new features - return !top[F("relay-0")][FPSTR(_button)].isNull(); + return !top[FPSTR(_broadcast)].isNull(); } /** @@ -570,3 +640,5 @@ const char MultiRelay::_delay_str[] PROGMEM = "delay-s"; const char MultiRelay::_activeHigh[] PROGMEM = "active-high"; const char MultiRelay::_external[] PROGMEM = "external"; const char MultiRelay::_button[] PROGMEM = "button"; +const char MultiRelay::_broadcast[] PROGMEM = "broadcast-sec"; +const char MultiRelay::_HAautodiscovery[] PROGMEM = "HA-autodiscovery";