158 lines
4.7 KiB
C++
158 lines
4.7 KiB
C++
#pragma once
|
|
|
|
#include "wled.h"
|
|
#ifndef WLED_ENABLE_MQTT
|
|
#error "This user mod requires MQTT to be enabled."
|
|
#endif
|
|
|
|
#ifndef MQTTSWITCHPINS
|
|
#error "Please define MQTTSWITCHPINS in platformio_override.ini. e.g. -D MQTTSWITCHPINS="12, 0, 2" "
|
|
// The following define helps Eclipse's C++ parser but is never used in production due to the #error statement on the line before
|
|
#define MQTTSWITCHPINS 12, 0, 2
|
|
#endif
|
|
|
|
// Default behavior: All outputs active high
|
|
#ifndef MQTTSWITCHINVERT
|
|
#define MQTTSWITCHINVERT
|
|
#endif
|
|
|
|
// Default behavior: All outputs off
|
|
#ifndef MQTTSWITCHDEFAULTS
|
|
#define MQTTSWITCHDEFAULTS
|
|
#endif
|
|
|
|
static const uint8_t switchPins[] = { MQTTSWITCHPINS };
|
|
//This is a hack to get the number of pins defined by the user
|
|
#define NUM_SWITCH_PINS (sizeof(switchPins))
|
|
static const bool switchInvert[NUM_SWITCH_PINS] = { MQTTSWITCHINVERT};
|
|
//Make settings in config file more readable
|
|
#define ON 1
|
|
#define OFF 0
|
|
static const bool switchDefaults[NUM_SWITCH_PINS] = { MQTTSWITCHDEFAULTS};
|
|
#undef ON
|
|
#undef OFF
|
|
|
|
class UsermodMqttSwitch: public Usermod
|
|
{
|
|
private:
|
|
bool mqttInitialized;
|
|
bool switchState[NUM_SWITCH_PINS];
|
|
|
|
public:
|
|
UsermodMqttSwitch() :
|
|
mqttInitialized(false)
|
|
{
|
|
}
|
|
|
|
void setup()
|
|
{
|
|
for (int pinNr = 0; pinNr < NUM_SWITCH_PINS; pinNr++) {
|
|
setState(pinNr, switchDefaults[pinNr]);
|
|
pinMode(switchPins[pinNr], OUTPUT);
|
|
}
|
|
}
|
|
|
|
void loop()
|
|
{
|
|
if (!mqttInitialized) {
|
|
mqttInit();
|
|
return; // Try again in next loop iteration
|
|
}
|
|
}
|
|
|
|
void mqttInit()
|
|
{
|
|
if (!mqtt)
|
|
return;
|
|
mqtt->onMessage(
|
|
std::bind(&UsermodMqttSwitch::onMqttMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4,
|
|
std::placeholders::_5, std::placeholders::_6));
|
|
mqtt->onConnect(std::bind(&UsermodMqttSwitch::onMqttConnect, this, std::placeholders::_1));
|
|
mqttInitialized = true;
|
|
}
|
|
|
|
void onMqttConnect(bool sessionPresent);
|
|
|
|
void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total);
|
|
void updateState(uint8_t pinNr);
|
|
|
|
void setState(uint8_t pinNr, bool active)
|
|
{
|
|
if (pinNr > NUM_SWITCH_PINS)
|
|
return;
|
|
switchState[pinNr] = active;
|
|
digitalWrite((char) switchPins[pinNr], (char) (switchInvert[pinNr] ? !active : active));
|
|
updateState(pinNr);
|
|
}
|
|
};
|
|
|
|
inline void UsermodMqttSwitch::onMqttConnect(bool sessionPresent)
|
|
{
|
|
if (mqttDeviceTopic[0] == 0)
|
|
return;
|
|
|
|
for (int pinNr = 0; pinNr < NUM_SWITCH_PINS; pinNr++) {
|
|
char buf[128];
|
|
StaticJsonDocument<1024> json;
|
|
sprintf(buf, "%s Switch %d", serverDescription, pinNr + 1);
|
|
json[F("name")] = buf;
|
|
|
|
sprintf(buf, "%s/switch/%d", mqttDeviceTopic, pinNr);
|
|
json["~"] = buf;
|
|
strcat(buf, "/set");
|
|
mqtt->subscribe(buf, 0);
|
|
|
|
json[F("stat_t")] = "~/state";
|
|
json[F("cmd_t")] = "~/set";
|
|
json[F("pl_off")] = F("OFF");
|
|
json[F("pl_on")] = F("ON");
|
|
|
|
char uid[16];
|
|
sprintf(uid, "%s_sw%d", escapedMac.c_str(), pinNr);
|
|
json[F("unique_id")] = uid;
|
|
|
|
strcpy(buf, mqttDeviceTopic);
|
|
strcat(buf, "/status");
|
|
json[F("avty_t")] = buf;
|
|
json[F("pl_avail")] = F("online");
|
|
json[F("pl_not_avail")] = F("offline");
|
|
//TODO: dev
|
|
sprintf(buf, "homeassistant/switch/%s/config", uid);
|
|
char json_str[1024];
|
|
size_t payload_size = serializeJson(json, json_str);
|
|
mqtt->publish(buf, 0, true, json_str, payload_size);
|
|
updateState(pinNr);
|
|
}
|
|
}
|
|
|
|
inline void UsermodMqttSwitch::onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total)
|
|
{
|
|
//Note: Payload is not necessarily null terminated. Check "len" instead.
|
|
for (int pinNr = 0; pinNr < NUM_SWITCH_PINS; pinNr++) {
|
|
char buf[64];
|
|
sprintf(buf, "%s/switch/%d/set", mqttDeviceTopic, pinNr);
|
|
if (strcmp(topic, buf) == 0) {
|
|
//Any string starting with "ON" is interpreted as ON, everything else as OFF
|
|
setState(pinNr, len >= 2 && payload[0] == 'O' && payload[1] == 'N');
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void UsermodMqttSwitch::updateState(uint8_t pinNr)
|
|
{
|
|
if (!mqttInitialized)
|
|
return;
|
|
|
|
if (pinNr > NUM_SWITCH_PINS)
|
|
return;
|
|
|
|
char buf[64];
|
|
sprintf(buf, "%s/switch/%d/state", mqttDeviceTopic, pinNr);
|
|
if (switchState[pinNr]) {
|
|
mqtt->publish(buf, 0, false, "ON");
|
|
} else {
|
|
mqtt->publish(buf, 0, false, "OFF");
|
|
}
|
|
}
|