Usermods MQTT processing.
Multi-relay usermod with MQTT/HTML control. Minor bugfixes.
This commit is contained in:
parent
ccab1844ce
commit
04c4451f7d
@ -6,23 +6,31 @@
|
|||||||
#define MULTI_RELAY_MAX_RELAYS 4
|
#define MULTI_RELAY_MAX_RELAYS 4
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define ON true
|
||||||
|
#define OFF false
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This usermod handles multiple relay outputs.
|
* This usermod handles multiple relay outputs.
|
||||||
* These outputs complement built-in relay output in a way that the activation can be delayed.
|
* These outputs complement built-in relay output in a way that the activation can be delayed.
|
||||||
* They can also activate/deactivate in reverse logic independently.
|
* They can also activate/deactivate in reverse logic independently.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct relay_t {
|
||||||
|
int8_t pin;
|
||||||
|
bool active;
|
||||||
|
bool mode;
|
||||||
|
bool state;
|
||||||
|
bool external;
|
||||||
|
uint16_t delay;
|
||||||
|
} Relay;
|
||||||
|
|
||||||
|
|
||||||
class MultiRelay : public Usermod {
|
class MultiRelay : public Usermod {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// pins
|
// array of relays
|
||||||
int8_t _relayPin[MULTI_RELAY_MAX_RELAYS];
|
Relay _relay[MULTI_RELAY_MAX_RELAYS];
|
||||||
// delay (in seconds) before relay state changes
|
|
||||||
uint16_t _relayDelay[MULTI_RELAY_MAX_RELAYS];
|
|
||||||
// activation mode (high/low)
|
|
||||||
bool _relayMode[MULTI_RELAY_MAX_RELAYS];
|
|
||||||
// relay active state
|
|
||||||
bool _relayActive[MULTI_RELAY_MAX_RELAYS];
|
|
||||||
|
|
||||||
// switch timer start time
|
// switch timer start time
|
||||||
uint32_t _switchTimerStart = 0;
|
uint32_t _switchTimerStart = 0;
|
||||||
@ -37,20 +45,10 @@ class MultiRelay : public Usermod {
|
|||||||
// strings to reduce flash memory usage (used more than twice)
|
// strings to reduce flash memory usage (used more than twice)
|
||||||
static const char _name[];
|
static const char _name[];
|
||||||
static const char _enabled[];
|
static const char _enabled[];
|
||||||
static const char _relay[];
|
static const char _relay_str[];
|
||||||
static const char _delay[];
|
static const char _delay_str[];
|
||||||
static const char _activeHigh[];
|
static const char _activeHigh[];
|
||||||
|
static const char _external[];
|
||||||
/**
|
|
||||||
* switch relay on/off
|
|
||||||
*/
|
|
||||||
void switchRelay(uint8_t relay) {
|
|
||||||
if (relay>=MULTI_RELAY_MAX_RELAYS || _relayPin[relay]<0) return;
|
|
||||||
pinMode(_relayPin[relay], OUTPUT);
|
|
||||||
bool mode = bri ? _relayMode[relay] : !_relayMode[relay];
|
|
||||||
digitalWrite(_relayPin[relay], mode);
|
|
||||||
publishMqtt(mode ? "on" : "off", relay);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void publishMqtt(const char* state, int relay) {
|
void publishMqtt(const char* state, int relay) {
|
||||||
@ -68,25 +66,110 @@ class MultiRelay : public Usermod {
|
|||||||
void handleOffTimer() {
|
void handleOffTimer() {
|
||||||
bool activeRelays = false;
|
bool activeRelays = false;
|
||||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||||
if (_relayActive[i] && _switchTimerStart > 0 && millis() - _switchTimerStart > (_relayDelay[i]*1000)) {
|
if (_relay[i].active && _switchTimerStart > 0 && millis() - _switchTimerStart > (_relay[i].delay*1000)) {
|
||||||
switchRelay(i); // toggle relay
|
if (!_relay[i].external) toggleRelay(i);
|
||||||
_relayActive[i] = false;
|
_relay[i].active = false;
|
||||||
}
|
}
|
||||||
activeRelays = activeRelays || _relayActive[i];
|
activeRelays = activeRelays || _relay[i].active;
|
||||||
}
|
}
|
||||||
if (!activeRelays) _switchTimerStart = 0;
|
if (!activeRelays) _switchTimerStart = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP API handler
|
||||||
|
* borrowed from:
|
||||||
|
* https://github.com/gsieben/WLED/blob/master/usermods/GeoGab-Relays/usermod_GeoGab.h
|
||||||
|
*/
|
||||||
|
#define GEOGABVERSION "0.1.3"
|
||||||
|
void InitHtmlAPIHandle() { // https://github.com/me-no-dev/ESPAsyncWebServer
|
||||||
|
DEBUG_PRINTLN(F("Relays: Initialize HTML API"));
|
||||||
|
|
||||||
|
server.on("/relays", HTTP_GET, [this](AsyncWebServerRequest *request) {
|
||||||
|
DEBUG_PRINTLN("Relays: HTML API");
|
||||||
|
String janswer;
|
||||||
|
String error = "";
|
||||||
|
int params = request->params();
|
||||||
|
janswer = F("{\"NoOfRelays\":");
|
||||||
|
janswer += String(MULTI_RELAY_MAX_RELAYS) + ",";
|
||||||
|
|
||||||
|
if (getActiveRelayCount()) {
|
||||||
|
// Commands
|
||||||
|
if(request->hasParam("switch")) {
|
||||||
|
/**** Switch ****/
|
||||||
|
AsyncWebParameter* p = request->getParam("switch");
|
||||||
|
// Get Values
|
||||||
|
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||||
|
int value = getValue(p->value(), ',', i);
|
||||||
|
if (value==-1) {
|
||||||
|
error = F("There must be as much arugments as relays");
|
||||||
|
} else {
|
||||||
|
// Switch
|
||||||
|
if (_relay[i].external) switchRelay(i, (bool)value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(request->hasParam("toggle")) {
|
||||||
|
/**** Toggle ****/
|
||||||
|
AsyncWebParameter* p = request->getParam("toggle");
|
||||||
|
// Get Values
|
||||||
|
for (int i=0;i<MULTI_RELAY_MAX_RELAYS;i++) {
|
||||||
|
int value = getValue(p->value(), ',', i);
|
||||||
|
if (value==-1) {
|
||||||
|
error = F("There must be as mutch arugments as relays");
|
||||||
|
} else {
|
||||||
|
// Toggle
|
||||||
|
if (value && _relay[i].external) toggleRelay(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error = F("No valid command found");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error = F("No active relays");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status response
|
||||||
|
char sbuf[16];
|
||||||
|
for (int i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||||
|
sprintf_P(sbuf, PSTR("\"%d\":%d,"), i, (_relay[i].pin<0 ? -1 : (int)_relay[i].state));
|
||||||
|
janswer += sbuf;
|
||||||
|
}
|
||||||
|
janswer += F("\"error\":\"");
|
||||||
|
janswer += error;
|
||||||
|
janswer += F("\",");
|
||||||
|
janswer += F("\"SW Version\":\"");
|
||||||
|
janswer += String(GEOGABVERSION);
|
||||||
|
janswer += F("\"}");
|
||||||
|
request->send(200, "application/json", janswer);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
int getValue(String data, char separator, int index) {
|
||||||
|
int found = 0;
|
||||||
|
int strIndex[] = {0, -1};
|
||||||
|
int maxIndex = data.length()-1;
|
||||||
|
|
||||||
|
for(int i=0; i<=maxIndex && found<=index; i++){
|
||||||
|
if(data.charAt(i)==separator || i==maxIndex){
|
||||||
|
found++;
|
||||||
|
strIndex[0] = strIndex[1]+1;
|
||||||
|
strIndex[1] = (i == maxIndex) ? i+1 : i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return found>index ? data.substring(strIndex[0], strIndex[1]).toInt() : -1;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* constructor
|
* constructor
|
||||||
*/
|
*/
|
||||||
MultiRelay() {
|
MultiRelay() {
|
||||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||||
_relayPin[i] = -1;
|
_relay[i].pin = -1;
|
||||||
_relayMode[i] = false;
|
_relay[i].delay = 0;
|
||||||
_relayDelay[i] = 0;
|
_relay[i].mode = false;
|
||||||
_relayActive[i] = false;
|
_relay[i].active = false;
|
||||||
|
_relay[i].state = false;
|
||||||
|
_relay[i].external = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@ -97,14 +180,76 @@ class MultiRelay : public Usermod {
|
|||||||
/**
|
/**
|
||||||
* Enable/Disable the usermod
|
* Enable/Disable the usermod
|
||||||
*/
|
*/
|
||||||
void enable(bool enable) { enabled = enable; }
|
inline void enable(bool enable) { enabled = enable; }
|
||||||
/**
|
/**
|
||||||
* Get usermod enabled/disabled state
|
* Get usermod enabled/disabled state
|
||||||
*/
|
*/
|
||||||
bool isEnabled() { return enabled; }
|
inline bool isEnabled() { return enabled; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* switch relay on/off
|
||||||
|
*/
|
||||||
|
void switchRelay(uint8_t relay, bool mode) {
|
||||||
|
if (relay>=MULTI_RELAY_MAX_RELAYS || _relay[relay].pin<0) return;
|
||||||
|
_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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* toggle relay
|
||||||
|
*/
|
||||||
|
inline void toggleRelay(uint8_t relay) {
|
||||||
|
switchRelay(relay, !_relay[relay].state);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t getActiveRelayCount() {
|
||||||
|
uint8_t count = 0;
|
||||||
|
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) if (_relay[i].pin>=0) count++;
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
//Functions called by WLED
|
//Functions called by WLED
|
||||||
|
|
||||||
|
/**
|
||||||
|
* handling of MQTT message
|
||||||
|
* topic only contains stripped topic (part after /wled/MAC)
|
||||||
|
* topic should look like: /relay/X/command; where X is relay number, 0 based
|
||||||
|
*/
|
||||||
|
bool onMqttMessage(char* topic, char* payload) {
|
||||||
|
if (strlen(topic) > 8 && strncmp_P(topic, PSTR("/relay/"), 7) == 0 && strncmp_P(topic+8, PSTR("/command"), 8) == 0) {
|
||||||
|
uint8_t relay = strtoul(topic+7, NULL, 10);
|
||||||
|
if (relay<MULTI_RELAY_MAX_RELAYS) {
|
||||||
|
String action = payload;
|
||||||
|
if (action == "on") {
|
||||||
|
if (_relay[relay].external) switchRelay(relay, true);
|
||||||
|
return true;
|
||||||
|
} else if (action == "off") {
|
||||||
|
if (_relay[relay].external) switchRelay(relay, false);
|
||||||
|
return true;
|
||||||
|
} else if (action == "toggle") {
|
||||||
|
if (_relay[relay].external) toggleRelay(relay);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* subscribe to MQTT topic for controlling relays
|
||||||
|
*/
|
||||||
|
void onMqttConnect(bool sessionPresent) {
|
||||||
|
//(re)subscribe to required topics
|
||||||
|
char subuf[64];
|
||||||
|
if (mqttDeviceTopic[0] != 0) {
|
||||||
|
strcpy(subuf, mqttDeviceTopic);
|
||||||
|
strcat_P(subuf, PSTR("/relay/#"));
|
||||||
|
mqtt->subscribe(subuf, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* setup() is called once at boot. WiFi is not yet connected at this point.
|
* setup() is called once at boot. WiFi is not yet connected at this point.
|
||||||
* You can use it to initialize variables, sensors or similar.
|
* You can use it to initialize variables, sensors or similar.
|
||||||
@ -112,12 +257,12 @@ class MultiRelay : public Usermod {
|
|||||||
void setup() {
|
void setup() {
|
||||||
// pins retrieved from cfg.json (readFromConfig()) prior to running setup()
|
// pins retrieved from cfg.json (readFromConfig()) prior to running setup()
|
||||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||||
if (_relayPin[i]<0) continue;
|
if (_relay[i].pin<0) continue;
|
||||||
if (!pinManager.allocatePin(_relayPin[i],true)) {
|
if (!pinManager.allocatePin(_relay[i].pin,true)) {
|
||||||
_relayPin[i] = -1; // allocation failed
|
_relay[i].pin = -1; // allocation failed
|
||||||
} else {
|
} else {
|
||||||
switchRelay(i);
|
switchRelay(i, _relay[i].state = (bool)bri);
|
||||||
_relayActive[i] = false;
|
_relay[i].active = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_oldBrightness = bri;
|
_oldBrightness = bri;
|
||||||
@ -128,7 +273,9 @@ class MultiRelay : public Usermod {
|
|||||||
* connected() is called every time the WiFi is (re)connected
|
* connected() is called every time the WiFi is (re)connected
|
||||||
* Use it to initialize network interfaces
|
* Use it to initialize network interfaces
|
||||||
*/
|
*/
|
||||||
void connected() {}
|
void connected() {
|
||||||
|
InitHtmlAPIHandle();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* loop() is called continuously. Here you can check for events, read sensors, etc.
|
* loop() is called continuously. Here you can check for events, read sensors, etc.
|
||||||
@ -145,7 +292,7 @@ class MultiRelay : public Usermod {
|
|||||||
_oldBrightness = bri;
|
_oldBrightness = bri;
|
||||||
_switchTimerStart = millis();
|
_switchTimerStart = millis();
|
||||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||||
if (_relayPin[i]>=0) _relayActive[i] = true;
|
if (_relay[i].pin>=0) _relay[i].active = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,15 +304,12 @@ class MultiRelay : public Usermod {
|
|||||||
*/
|
*/
|
||||||
void addToJsonInfo(JsonObject &root) {
|
void addToJsonInfo(JsonObject &root) {
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
uint8_t count = 0;
|
|
||||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) if (_relayPin[i]>=0) count++;
|
|
||||||
|
|
||||||
JsonObject user = root["u"];
|
JsonObject user = root["u"];
|
||||||
if (user.isNull())
|
if (user.isNull())
|
||||||
user = root.createNestedObject("u");
|
user = root.createNestedObject("u");
|
||||||
|
|
||||||
JsonArray infoArr = user.createNestedArray(F("Number of relays")); //name
|
JsonArray infoArr = user.createNestedArray(F("Number of relays")); //name
|
||||||
infoArr.add(String(count));
|
infoArr.add(String(getActiveRelayCount()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,10 +335,11 @@ class MultiRelay : public Usermod {
|
|||||||
|
|
||||||
top[FPSTR(_enabled)] = enabled;
|
top[FPSTR(_enabled)] = enabled;
|
||||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||||
String parName = FPSTR(_relay); parName += "-"; parName += i; parName += "-";
|
String parName = FPSTR(_relay_str); parName += "-"; parName += i; parName += "-";
|
||||||
top[parName+"pin"] = _relayPin[i];
|
top[parName+"pin"] = _relay[i].pin;
|
||||||
top[parName+FPSTR(_activeHigh)] = _relayMode[i];
|
top[parName+FPSTR(_activeHigh)] = _relay[i].mode;
|
||||||
top[parName+FPSTR(_delay)] = _relayDelay[i];
|
top[parName+FPSTR(_delay_str)] = _relay[i].delay;
|
||||||
|
top[parName+FPSTR(_external)] = _relay[i].external;
|
||||||
}
|
}
|
||||||
DEBUG_PRINTLN(F("MultiRelay config saved."));
|
DEBUG_PRINTLN(F("MultiRelay config saved."));
|
||||||
}
|
}
|
||||||
@ -220,22 +365,32 @@ class MultiRelay : public Usermod {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||||
String parName = FPSTR(_relay); parName += "-"; parName += i; parName += "-";
|
String parName = FPSTR(_relay_str); parName += "-"; parName += i; parName += "-";
|
||||||
|
|
||||||
oldPin[i] = _relayPin[i];
|
oldPin[i] = _relay[i].pin;
|
||||||
if (top[parName+"pin"] != nullptr) _relayPin[i] = min(39,max(-1,top[parName+"pin"].as<int>()));
|
if (top[parName+"pin"] != nullptr) _relay[i].pin = min(39,max(-1,top[parName+"pin"].as<int>()));
|
||||||
|
|
||||||
if (top[parName+FPSTR(_activeHigh)] != nullptr) {
|
if (top[parName+FPSTR(_activeHigh)] != nullptr) {
|
||||||
if (top[parName+FPSTR(_activeHigh)].is<bool>()) {
|
if (top[parName+FPSTR(_activeHigh)].is<bool>()) {
|
||||||
_relayMode[i] = top[parName+FPSTR(_activeHigh)].as<bool>(); // reading from cfg.json
|
_relay[i].mode = top[parName+FPSTR(_activeHigh)].as<bool>(); // reading from cfg.json
|
||||||
} else {
|
} else {
|
||||||
// change from settings page
|
// change from settings page
|
||||||
String str = top[parName+FPSTR(_activeHigh)]; // checkbox -> off or on
|
String str = top[parName+FPSTR(_activeHigh)]; // checkbox -> off or on
|
||||||
_relayMode[i] = (bool)(str!="off"); // off is guaranteed to be present
|
_relay[i].mode = (bool)(str!="off"); // off is guaranteed to be present
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_relayDelay[i] = min(600,max(0,abs(top[parName+FPSTR(_delay)].as<int>())));
|
if (top[parName+FPSTR(_external)] != nullptr) {
|
||||||
|
if (top[parName+FPSTR(_external)].is<bool>()) {
|
||||||
|
_relay[i].external = top[parName+FPSTR(_external)].as<bool>(); // reading from cfg.json
|
||||||
|
} else {
|
||||||
|
// change from settings page
|
||||||
|
String str = top[parName+FPSTR(_external)]; // checkbox -> off or on
|
||||||
|
_relay[i].external = (bool)(str!="off"); // off is guaranteed to be present
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_relay[i].delay = min(600,max(0,abs(top[parName+FPSTR(_delay_str)].as<int>())));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!initDone) {
|
if (!initDone) {
|
||||||
@ -249,12 +404,12 @@ class MultiRelay : public Usermod {
|
|||||||
}
|
}
|
||||||
// allocate new pins
|
// allocate new pins
|
||||||
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
for (uint8_t i=0; i<MULTI_RELAY_MAX_RELAYS; i++) {
|
||||||
if (_relayPin[i]>=0 && pinManager.allocatePin(_relayPin[i],true)) {
|
if (_relay[i].pin>=0 && pinManager.allocatePin(_relay[i].pin,true)) {
|
||||||
switchRelay(i);
|
if (!_relay[i].external) switchRelay(i, _relay[i].state = (bool)bri);
|
||||||
} else {
|
} else {
|
||||||
_relayPin[i] = -1;
|
_relay[i].pin = -1;
|
||||||
}
|
}
|
||||||
_relayActive[i] = false;
|
_relay[i].active = false;
|
||||||
}
|
}
|
||||||
DEBUG_PRINTLN(F("MultiRelay config (re)loaded."));
|
DEBUG_PRINTLN(F("MultiRelay config (re)loaded."));
|
||||||
}
|
}
|
||||||
@ -273,6 +428,7 @@ class MultiRelay : public Usermod {
|
|||||||
// strings to reduce flash memory usage (used more than twice)
|
// strings to reduce flash memory usage (used more than twice)
|
||||||
const char MultiRelay::_name[] PROGMEM = "MultiRelay";
|
const char MultiRelay::_name[] PROGMEM = "MultiRelay";
|
||||||
const char MultiRelay::_enabled[] PROGMEM = "enabled";
|
const char MultiRelay::_enabled[] PROGMEM = "enabled";
|
||||||
const char MultiRelay::_relay[] PROGMEM = "relay";
|
const char MultiRelay::_relay_str[] PROGMEM = "relay";
|
||||||
const char MultiRelay::_delay[] PROGMEM = "delay-s";
|
const char MultiRelay::_delay_str[] PROGMEM = "delay-s";
|
||||||
const char MultiRelay::_activeHigh[] PROGMEM = "active-high";
|
const char MultiRelay::_activeHigh[] PROGMEM = "active-high";
|
||||||
|
const char MultiRelay::_external[] PROGMEM = "external";
|
||||||
|
@ -97,7 +97,7 @@ class AutoSaveUsermod : public Usermod {
|
|||||||
* Da loop.
|
* Da loop.
|
||||||
*/
|
*/
|
||||||
void loop() {
|
void loop() {
|
||||||
if (!autoSaveAfterSec && !enabled) return; // setting 0 as autosave seconds disables autosave
|
if (!autoSaveAfterSec || !enabled) return; // setting 0 as autosave seconds disables autosave
|
||||||
|
|
||||||
unsigned long now = millis();
|
unsigned long now = millis();
|
||||||
uint8_t currentMode = strip.getMode();
|
uint8_t currentMode = strip.getMode();
|
||||||
|
@ -187,27 +187,27 @@ class Usermod {
|
|||||||
virtual void readFromJsonState(JsonObject& obj) {}
|
virtual void readFromJsonState(JsonObject& obj) {}
|
||||||
virtual void addToConfig(JsonObject& obj) {}
|
virtual void addToConfig(JsonObject& obj) {}
|
||||||
virtual void readFromConfig(JsonObject& obj) {}
|
virtual void readFromConfig(JsonObject& obj) {}
|
||||||
|
virtual void onMqttConnect(bool sessionPresent) {}
|
||||||
|
virtual bool onMqttMessage(char* topic, char* payload) { return false; }
|
||||||
virtual uint16_t getId() {return USERMOD_ID_UNSPECIFIED;}
|
virtual uint16_t getId() {return USERMOD_ID_UNSPECIFIED;}
|
||||||
};
|
};
|
||||||
|
|
||||||
class UsermodManager {
|
class UsermodManager : public Usermod {
|
||||||
private:
|
private:
|
||||||
Usermod* ums[WLED_MAX_USERMODS];
|
Usermod* ums[WLED_MAX_USERMODS];
|
||||||
byte numMods = 0;
|
byte numMods = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void loop();
|
void loop();
|
||||||
|
|
||||||
void setup();
|
void setup();
|
||||||
void connected();
|
void connected();
|
||||||
|
|
||||||
void addToJsonState(JsonObject& obj);
|
void addToJsonState(JsonObject& obj);
|
||||||
void addToJsonInfo(JsonObject& obj);
|
void addToJsonInfo(JsonObject& obj);
|
||||||
void readFromJsonState(JsonObject& obj);
|
void readFromJsonState(JsonObject& obj);
|
||||||
|
|
||||||
void addToConfig(JsonObject& obj);
|
void addToConfig(JsonObject& obj);
|
||||||
void readFromConfig(JsonObject& obj);
|
void readFromConfig(JsonObject& obj);
|
||||||
|
void onMqttConnect(bool sessionPresent);
|
||||||
|
bool onMqttMessage(char* topic, char* payload);
|
||||||
bool add(Usermod* um);
|
bool add(Usermod* um);
|
||||||
Usermod* lookup(uint16_t mod_id);
|
Usermod* lookup(uint16_t mod_id);
|
||||||
byte getModCount();
|
byte getModCount();
|
||||||
|
@ -25,28 +25,28 @@ void onMqttConnect(bool sessionPresent)
|
|||||||
//(re)subscribe to required topics
|
//(re)subscribe to required topics
|
||||||
char subuf[38];
|
char subuf[38];
|
||||||
|
|
||||||
if (mqttDeviceTopic[0] != 0)
|
if (mqttDeviceTopic[0] != 0) {
|
||||||
{
|
|
||||||
strcpy(subuf, mqttDeviceTopic);
|
strcpy(subuf, mqttDeviceTopic);
|
||||||
mqtt->subscribe(subuf, 0);
|
mqtt->subscribe(subuf, 0);
|
||||||
strcat(subuf, "/col");
|
strcat_P(subuf, PSTR("/col"));
|
||||||
mqtt->subscribe(subuf, 0);
|
mqtt->subscribe(subuf, 0);
|
||||||
strcpy(subuf, mqttDeviceTopic);
|
strcpy(subuf, mqttDeviceTopic);
|
||||||
strcat(subuf, "/api");
|
strcat_P(subuf, PSTR("/api"));
|
||||||
mqtt->subscribe(subuf, 0);
|
mqtt->subscribe(subuf, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mqttGroupTopic[0] != 0)
|
if (mqttGroupTopic[0] != 0) {
|
||||||
{
|
|
||||||
strcpy(subuf, mqttGroupTopic);
|
strcpy(subuf, mqttGroupTopic);
|
||||||
mqtt->subscribe(subuf, 0);
|
mqtt->subscribe(subuf, 0);
|
||||||
strcat(subuf, "/col");
|
strcat_P(subuf, PSTR("/col"));
|
||||||
mqtt->subscribe(subuf, 0);
|
mqtt->subscribe(subuf, 0);
|
||||||
strcpy(subuf, mqttGroupTopic);
|
strcpy(subuf, mqttGroupTopic);
|
||||||
strcat(subuf, "/api");
|
strcat_P(subuf, PSTR("/api"));
|
||||||
mqtt->subscribe(subuf, 0);
|
mqtt->subscribe(subuf, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
usermods.onMqttConnect(sessionPresent);
|
||||||
|
|
||||||
doPublishMqtt = true;
|
doPublishMqtt = true;
|
||||||
DEBUG_PRINTLN(F("MQTT ready"));
|
DEBUG_PRINTLN(F("MQTT ready"));
|
||||||
}
|
}
|
||||||
@ -66,25 +66,24 @@ void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties
|
|||||||
|
|
||||||
size_t topicPrefixLen = strlen(mqttDeviceTopic);
|
size_t topicPrefixLen = strlen(mqttDeviceTopic);
|
||||||
if (strncmp(topic, mqttDeviceTopic, topicPrefixLen) == 0) {
|
if (strncmp(topic, mqttDeviceTopic, topicPrefixLen) == 0) {
|
||||||
topic += topicPrefixLen;
|
topic += topicPrefixLen;
|
||||||
} else {
|
} else {
|
||||||
topicPrefixLen = strlen(mqttGroupTopic);
|
topicPrefixLen = strlen(mqttGroupTopic);
|
||||||
if (strncmp(topic, mqttGroupTopic, topicPrefixLen) == 0) {
|
if (strncmp(topic, mqttGroupTopic, topicPrefixLen) == 0) {
|
||||||
topic += topicPrefixLen;
|
topic += topicPrefixLen;
|
||||||
} else {
|
} else {
|
||||||
// Topic not used here. Probably a usermod subscribed to this topic.
|
// Non-Wled Topic used here. Probably a usermod subscribed to this topic.
|
||||||
return;
|
usermods.onMqttMessage(topic, payload);
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Prefix is stripped from the topic at this point
|
//Prefix is stripped from the topic at this point
|
||||||
|
|
||||||
if (strcmp(topic, "/col") == 0)
|
if (strcmp_P(topic, PSTR("/col")) == 0) {
|
||||||
{
|
|
||||||
colorFromDecOrHexString(col, (char*)payload);
|
colorFromDecOrHexString(col, (char*)payload);
|
||||||
colorUpdated(NOTIFIER_CALL_MODE_DIRECT_CHANGE);
|
colorUpdated(NOTIFIER_CALL_MODE_DIRECT_CHANGE);
|
||||||
} else if (strcmp(topic, "/api") == 0)
|
} else if (strcmp_P(topic, PSTR("/api")) == 0) {
|
||||||
{
|
|
||||||
if (payload[0] == '{') { //JSON API
|
if (payload[0] == '{') { //JSON API
|
||||||
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
|
DynamicJsonDocument doc(JSON_BUFFER_SIZE);
|
||||||
deserializeJson(doc, payload);
|
deserializeJson(doc, payload);
|
||||||
@ -94,8 +93,11 @@ void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties
|
|||||||
apireq += (char*)payload;
|
apireq += (char*)payload;
|
||||||
handleSet(nullptr, apireq);
|
handleSet(nullptr, apireq);
|
||||||
}
|
}
|
||||||
} else if (strcmp(topic, "") == 0)
|
} else if (strlen(topic) != 0) {
|
||||||
{
|
// non standard topic, check with usermods
|
||||||
|
usermods.onMqttMessage(topic, payload);
|
||||||
|
} else {
|
||||||
|
// topmost topic (just wled/MAC)
|
||||||
parseMQTTBriPayload(payload);
|
parseMQTTBriPayload(payload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,24 +112,24 @@ void publishMqtt()
|
|||||||
char s[10];
|
char s[10];
|
||||||
char subuf[38];
|
char subuf[38];
|
||||||
|
|
||||||
sprintf(s, "%u", bri);
|
sprintf_P(s, PSTR("%u"), bri);
|
||||||
strcpy(subuf, mqttDeviceTopic);
|
strcpy(subuf, mqttDeviceTopic);
|
||||||
strcat(subuf, "/g");
|
strcat_P(subuf, PSTR("/g"));
|
||||||
mqtt->publish(subuf, 0, true, s);
|
mqtt->publish(subuf, 0, true, s);
|
||||||
|
|
||||||
sprintf(s, "#%06X", (col[3] << 24) | (col[0] << 16) | (col[1] << 8) | (col[2]));
|
sprintf_P(s, PSTR("#%06X"), (col[3] << 24) | (col[0] << 16) | (col[1] << 8) | (col[2]));
|
||||||
strcpy(subuf, mqttDeviceTopic);
|
strcpy(subuf, mqttDeviceTopic);
|
||||||
strcat(subuf, "/c");
|
strcat_P(subuf, PSTR("/c"));
|
||||||
mqtt->publish(subuf, 0, true, s);
|
mqtt->publish(subuf, 0, true, s);
|
||||||
|
|
||||||
strcpy(subuf, mqttDeviceTopic);
|
strcpy(subuf, mqttDeviceTopic);
|
||||||
strcat(subuf, "/status");
|
strcat_P(subuf, PSTR("/status"));
|
||||||
mqtt->publish(subuf, 0, true, "online");
|
mqtt->publish(subuf, 0, true, "online");
|
||||||
|
|
||||||
char apires[1024];
|
char apires[1024];
|
||||||
XML_response(nullptr, apires);
|
XML_response(nullptr, apires);
|
||||||
strcpy(subuf, mqttDeviceTopic);
|
strcpy(subuf, mqttDeviceTopic);
|
||||||
strcat(subuf, "/v");
|
strcat_P(subuf, PSTR("/v"));
|
||||||
mqtt->publish(subuf, 0, true, apires);
|
mqtt->publish(subuf, 0, true, apires);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,7 +159,7 @@ bool initMqtt()
|
|||||||
if (mqttUser[0] && mqttPass[0]) mqtt->setCredentials(mqttUser, mqttPass);
|
if (mqttUser[0] && mqttPass[0]) mqtt->setCredentials(mqttUser, mqttPass);
|
||||||
|
|
||||||
strcpy(mqttStatusTopic, mqttDeviceTopic);
|
strcpy(mqttStatusTopic, mqttDeviceTopic);
|
||||||
strcat(mqttStatusTopic, "/status");
|
strcat_P(mqttStatusTopic, PSTR("/status"));
|
||||||
mqtt->setWill(mqttStatusTopic, 0, true, "offline");
|
mqtt->setWill(mqttStatusTopic, 0, true, "offline");
|
||||||
mqtt->setKeepAlive(MQTT_KEEP_ALIVE_TIME);
|
mqtt->setKeepAlive(MQTT_KEEP_ALIVE_TIME);
|
||||||
mqtt->connect();
|
mqtt->connect();
|
||||||
|
@ -14,6 +14,11 @@ void UsermodManager::addToJsonInfo(JsonObject& obj) { for (byte i = 0; i < n
|
|||||||
void UsermodManager::readFromJsonState(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->readFromJsonState(obj); }
|
void UsermodManager::readFromJsonState(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->readFromJsonState(obj); }
|
||||||
void UsermodManager::addToConfig(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->addToConfig(obj); }
|
void UsermodManager::addToConfig(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->addToConfig(obj); }
|
||||||
void UsermodManager::readFromConfig(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->readFromConfig(obj); }
|
void UsermodManager::readFromConfig(JsonObject& obj) { for (byte i = 0; i < numMods; i++) ums[i]->readFromConfig(obj); }
|
||||||
|
void UsermodManager::onMqttConnect(bool sessionPresent) { for (byte i = 0; i < numMods; i++) ums[i]->onMqttConnect(sessionPresent); }
|
||||||
|
bool UsermodManager::onMqttMessage(char* topic, char* payload) {
|
||||||
|
for (byte i = 0; i < numMods; i++) if (ums[i]->onMqttMessage(topic, payload)) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Enables usermods to lookup another Usermod.
|
* Enables usermods to lookup another Usermod.
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// version code in format yymmddb (b = daily build)
|
// version code in format yymmddb (b = daily build)
|
||||||
#define VERSION 2105041
|
#define VERSION 2105061
|
||||||
|
|
||||||
//uncomment this if you have a "my_config.h" file you'd like to use
|
//uncomment this if you have a "my_config.h" file you'd like to use
|
||||||
//#define WLED_USE_MY_CONFIG
|
//#define WLED_USE_MY_CONFIG
|
||||||
|
Loading…
Reference in New Issue
Block a user