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!
diff --git a/wled00/html_settings.h b/wled00/html_settings.h index ffb1b3ff..434299e2 100644 --- a/wled00/html_settings.h +++ b/wled00/html_settings.h @@ -360,7 +360,7 @@ HTTP traffic is unencrypted. An attacker in the same network can intercept form href="https://github.com/Aircoookie/WLED/wiki/Contributors-&-About" target="_blank">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!
)====="; diff --git a/wled00/json.cpp b/wled00/json.cpp index be1a87e3..69d6633b 100644 --- a/wled00/json.cpp +++ b/wled00/json.cpp @@ -338,6 +338,11 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme nl[F("fade")] = (nightlightMode > NL_MODE_SET); //deprecated nl[F("mode")] = nightlightMode; nl[F("tbri")] = nightlightTargetBri; + if (nightlightActive) { + nl[F("rem")] = (nightlightDelayMs - (millis() - nightlightStartTime)) / 1000; // seconds remaining + } else { + nl[F("rem")] = -1; + } JsonObject udpn = root.createNestedObject("udpn"); udpn[F("send")] = notifyDirect; @@ -347,7 +352,7 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme } root[F("mainseg")] = strip.getMainSegmentId(); - + JsonArray seg = root.createNestedArray("seg"); for (byte s = 0; s < strip.getMaxSegments(); s++) { diff --git a/wled00/mqtt.cpp b/wled00/mqtt.cpp index 46f49e19..15cb0c5d 100644 --- a/wled00/mqtt.cpp +++ b/wled00/mqtt.cpp @@ -64,18 +64,40 @@ void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties } DEBUG_PRINTLN(payload); - //no need to check the topic because we only get topics we are subscribed to + size_t topicPrefixLen = strlen(mqttDeviceTopic); + if (strncmp(topic, mqttDeviceTopic, topicPrefixLen) == 0) { + topic += topicPrefixLen; + } else { + size_t topic_prefix_len = strlen(mqttGroupTopic); + if (strncmp(topic, mqttGroupTopic, topicPrefixLen) == 0) { + topic += topicPrefixLen; + } else { + // Topic not used here. Probably a usermod subscribed to this topic. + return; + } + } - if (strstr(topic, "/col")) + //Prefix is stripped from the topic at this point + + if (strcmp(topic, "/col") == 0) { colorFromDecOrHexString(col, (char*)payload); colorUpdated(NOTIFIER_CALL_MODE_DIRECT_CHANGE); - } else if (strstr(topic, "/api")) + } else if (strcmp(topic, "/api") == 0) { - String apireq = "win&"; - apireq += (char*)payload; - handleSet(nullptr, apireq); - } else parseMQTTBriPayload(payload); + if (payload[0] == '{') { //JSON API + DynamicJsonDocument doc(JSON_BUFFER_SIZE); + deserializeJson(doc, payload); + deserializeState(doc.as()); + } else { //HTTP API + String apireq = "win&"; + apireq += (char*)payload; + handleSet(nullptr, apireq); + } + } else if (strcmp(topic, "") == 0) + { + parseMQTTBriPayload(payload); + } } diff --git a/wled00/palettes.h b/wled00/palettes.h index 4fca9525..30d28433 100644 --- a/wled00/palettes.h +++ b/wled00/palettes.h @@ -13,7 +13,7 @@ #ifndef PalettesWLED_h #define PalettesWLED_h -#define GRADIENT_PALETTE_COUNT 40 +#define GRADIENT_PALETTE_COUNT 41 const byte ib_jul01_gp[] PROGMEM = { 0, 194, 1, 1, @@ -551,14 +551,14 @@ const byte Orangery_gp[] PROGMEM = { //inspired by Mark Kriegsman https://gist.github.com/kriegsman/756ea6dcae8e30845b5a const byte C9_gp[] PROGMEM = { - 0, 255, 5, 0, //red - 60, 255, 5, 0, - 60, 196, 57, 2, //amber (start 61?) - 120, 196, 57, 2, - 120, 6, 126, 2, //green (start 126?) - 180, 6, 126, 2, - 180, 4, 30, 114, //blue (start 191?) - 255, 4, 30, 114}; + 0, 184, 4, 0, //red + 60, 184, 4, 0, + 65, 144, 44, 2, //amber + 125, 144, 44, 2, + 130, 4, 96, 2, //green + 190, 4, 96, 2, + 195, 7, 7, 88, //blue + 255, 7, 7, 88}; const byte Sakura_gp[] PROGMEM = { 0, 196, 19, 10, @@ -594,6 +594,17 @@ const byte Atlantica_gp[] PROGMEM = { 180, 196, 57, 2, 180, 137, 85, 2, //yellow 255, 137, 85, 2}; + + //C9, but brighter and with a less purple blue + const byte C9_new_gp[] PROGMEM = { + 0, 255, 5, 0, //red + 60, 255, 5, 0, + 60, 196, 57, 2, //amber (start 61?) + 120, 196, 57, 2, + 120, 6, 126, 2, //green (start 126?) + 180, 6, 126, 2, + 180, 4, 30, 114, //blue (start 191?) + 255, 4, 30, 114}; // Single array of defined cpt-city color palettes. @@ -640,7 +651,8 @@ const byte* const gGradientPalettes[] PROGMEM = { Sakura_gp, //49-36 Sakura Aurora_gp, //50-37 Aurora Atlantica_gp, //51-38 Atlantica - C9_2_gp //52-39 C9 2 + C9_2_gp, //52-39 C9 2 + C9_new_gp //53-40 C9 New }; #endif diff --git a/wled00/wled.h b/wled00/wled.h index 3b4f1da1..438f14dc 100644 --- a/wled00/wled.h +++ b/wled00/wled.h @@ -8,7 +8,7 @@ */ // version code in format yymmddb (b = daily build) -#define VERSION 2011092 +#define VERSION 2011121 // ESP8266-01 (blue) got too little storage space to work with WLED. 0.10.2 is the last release supporting this unit.