Skip to content

Commit

Permalink
New usermod: MQTT switches.
Browse files Browse the repository at this point in the history
This user mod adds a function to toggle output pins via MQTT.
  • Loading branch information
herm committed Nov 1, 2020
1 parent b694bcf commit fe3fb62
Show file tree
Hide file tree
Showing 2 changed files with 186 additions and 0 deletions.
46 changes: 46 additions & 0 deletions usermods/mqtt_switch_v2/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# MQTT controllable switches
This usermod allows controlling switches (e.g. relays) via MQTT.

## Usermod installation

1. Copy the file `usermod_mqtt_switch.h` to the `wled00` directory.
2. Register the usermod by adding `#include "usermod_mqtt_switch.h"` in the top and `registerUsermod(new UsermodMqttSwitch());` in the bottom of `usermods_list.cpp`.


Example `usermods_list.cpp`:

```
#include "wled.h"
#include "usermod_mqtt_switch.h"
void registerUsermods()
{
usermods.add(new UsermodMqttSwitch());
}
```

## Define pins
Add a define for MQTTSWITCHPINS to platformio_override.ini.
The following example defines 3 switches connected to the GPIO pins 13, 0 and 2:

```
[env:livingroom]
board = esp12e
platform = ${common.platform_wled_default}
board_build.ldscript = ${common.ldscript_4m1m}
build_flags = ${common.build_flags_esp8266}
-D LEDPIN=3
-D BTNPIN=4
-D RLYPIN=12
-D RLYMDE=1
-D STATUSPIN=15
-D MQTTSWITCHPINS="13, 0, 2"
```

## MQTT topics
This usermod listens on `[mqttDeviceTopic]/switch/0/set` (where 0 is replaced with the index of the switch) for commands. Anything starting with `ON` turns on the switch, everything else turns it off.
Feedback about the current state is provided at `[mqttDeviceTopic]/switch/0/state`.

### Home Assistant auto-discovery
Auto-discovery information is automatically published and you shoudn't have to do anything to register the switches in Home Assistant.

140 changes: 140 additions & 0 deletions usermods/mqtt_switch_v2/usermod_mqtt_switch.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
#pragma once

#include "wled.h"
#ifndef WLED_ENABLE_MQTT
#error "This user mod requires MQTT to be enabled."
#endif

#ifndef MQTTSWITCHPINS
#define MQTTSWITCHPINS 12, 0, 2
//#error "Please define MQTTSWITCHPINS in platformio_override.ini. e.g. -D MQTTSWITCHPINS="12, 0, 2" "
#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))

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++) {
pinMode(switchPins[pinNr], OUTPUT);
setState(pinNr, false);
}
}

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

0 comments on commit fe3fb62

Please sign in to comment.