diff --git a/CHANGELOG.md b/CHANGELOG.md
index 464e9288..4581c0ab 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,19 @@
### Development versions after the 0.10.2 release
+#### Build 2011120
+
+- Added the ability for the /api MQTT topic to receive JSON API payloads
+
+#### Build 2011040
+
+- Inversed Rain direction (fixes #1147)
+
+#### Build 2011010
+
+- Re-added previous C9 palette
+- Renamed new C9 palette
+
#### Build 2010290
- Colorful effect now supports palettes
diff --git a/readme.md b/readme.md
index a2e94a69..c3a1f2ad 100644
--- a/readme.md
+++ b/readme.md
@@ -11,7 +11,7 @@
# Welcome to my project WLED! ✨
-A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control NeoPixel (WS2812B, WS2811, SK6812, APA102) LEDs or also SPI based chipsets like the WS2801!
+A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control NeoPixel (WS2812B, WS2811, SK6812) LEDs or also SPI based chipsets like the WS2801 and APA102!
## ⚙️ Features
- WS2812FX library integrated for over 100 special effects
@@ -47,15 +47,7 @@ A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control
See the [wiki](https://github.com/Aircoookie/WLED/wiki)!
-DrZzs has made some excellent video guides:
-[Introduction, hardware and installation](https://www.youtube.com/watch?v=tXvtxwK3jRk)
-[Settings, tips and tricks](https://www.youtube.com/watch?v=6eCE2BpLaUQ)
-
-If you'd rather read, here is a very [detailed step-by-step beginner tutorial](https://tynick.com/blog/11-03-2019/getting-started-with-wled-on-esp8266/) by tynick!
-
-Russian speakers, check out the videos by Room31:
-[WLED Firmware Overview: Interface and Settings](https://youtu.be/h7lKsczEI7E)
-[ESP8266 based LED controller for WS2812b strip. WLED Firmware + OpenHAB](https://youtu.be/K4ioTt3XvGc)
+[On this page](https://github.com/Aircoookie/WLED/wiki/Learning-the-ropes) you can find excellent tutorials made by the community and helpful tools to help you get your new lamp up and running!
## 🖼️ Images
diff --git a/usermods/mqtt_switch_v2/README.md b/usermods/mqtt_switch_v2/README.md
new file mode 100644
index 00000000..dc0e259f
--- /dev/null
+++ b/usermods/mqtt_switch_v2/README.md
@@ -0,0 +1,50 @@
+# 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, 5 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, 5, 2"
+```
+
+Pins can be inverted by setting `MQTTSWITCHINVERT`. For example `-D MQTTSWITCHINVERT="false, false, true"` would invert the switch on pin 2 in the previous example.
+
+The default state after booting before any MQTT message can be set by `MQTTSWITCHDEFAULTS`. For example `-D MQTTSWITCHDEFAULTS="ON, OFF, OFF"` would power on the switch on pin 13 and power off switches on pins 5 and 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.
+
diff --git a/usermods/mqtt_switch_v2/usermod_mqtt_switch.h b/usermods/mqtt_switch_v2/usermod_mqtt_switch.h
new file mode 100644
index 00000000..40241206
--- /dev/null
+++ b/usermods/mqtt_switch_v2/usermod_mqtt_switch.h
@@ -0,0 +1,157 @@
+#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");
+ }
+}
diff --git a/wled00/FX.cpp b/wled00/FX.cpp
index 04913e22..bac929ff 100644
--- a/wled00/FX.cpp
+++ b/wled00/FX.cpp
@@ -1128,12 +1128,12 @@ uint16_t WS2812FX::mode_rain()
SEGENV.step += FRAMETIME;
if (SEGENV.step > SPEED_FORMULA_L) {
SEGENV.step = 0;
- //shift all leds right
- uint32_t ctemp = getPixelColor(SEGLEN -1);
- for(uint16_t i = SEGLEN -1; i > 0; i--) {
- setPixelColor(i, getPixelColor(i-1));
+ //shift all leds left
+ uint32_t ctemp = getPixelColor(0);
+ for(uint16_t i = 0; i < SEGLEN - 1; i++) {
+ setPixelColor(i, getPixelColor(i+1));
}
- setPixelColor(0, ctemp);
+ setPixelColor(SEGLEN -1, ctemp);
SEGENV.aux0++;
SEGENV.aux1++;
if (SEGENV.aux0 == 0) SEGENV.aux0 = UINT16_MAX;
diff --git a/wled00/FX.h b/wled00/FX.h
index fb39428d..b61fd505 100644
--- a/wled00/FX.h
+++ b/wled00/FX.h
@@ -731,7 +731,7 @@ const char JSON_palette_names[] PROGMEM = R"=====([
"Pastel","Sunset 2","Beech","Vintage","Departure","Landscape","Beach","Sherbet","Hult","Hult 64",
"Drywet","Jul","Grintage","Rewhi","Tertiary","Fire","Icefire","Cyane","Light Pink","Autumn",
"Magenta","Magred","Yelmag","Yelblu","Orange & Teal","Tiamat","April Night","Orangery","C9","Sakura",
-"Aurora","Atlantica","C9 2"
+"Aurora","Atlantica","C9 2","C9 New"
])=====";
#endif
diff --git a/wled00/data/settings_sec.htm b/wled00/data/settings_sec.htm
index 08d9427f..6a860e6b 100644
--- a/wled00/data/settings_sec.htm
+++ b/wled00/data/settings_sec.htm
@@ -48,8 +48,8 @@
WLED version ##VERSION##
Contributors, dependencies and special thanks
A huge thank you to everyone who helped me create WLED!
- (c) 2016-2019 Christian Schwinne
- Licensed under the MIT license
+ (c) 2016-2020 Christian Schwinne
+ Licensed under the MIT license
Server message: Response error!