kopia lustrzana https://github.com/Aircoookie/WLED
commit
aa8c587ac8
13
CHANGELOG.md
13
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
|
||||
|
|
12
readme.md
12
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
|
||||
<img src="/images/macbook-pro-space-gray-on-the-wooden-table.jpg" width="50%"><img src="/images/walking-with-iphone-x.jpg" width="50%">
|
||||
|
|
|
@ -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.
|
||||
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -48,8 +48,8 @@
|
|||
<a href="https://github.com/Aircoookie/WLED/" target="_blank">WLED</a> version ##VERSION##<!-- Autoreplaced from package.json --><br><br>
|
||||
<a href="https://github.com/Aircoookie/WLED/wiki/Contributors-&-About" target="_blank">Contributors, dependencies and special thanks</a><br>
|
||||
A huge thank you to everyone who helped me create WLED!<br><br>
|
||||
(c) 2016-2019 Christian Schwinne <br>
|
||||
<i>Licensed under the MIT license</i><br><br>
|
||||
(c) 2016-2020 Christian Schwinne <br>
|
||||
<i>Licensed under the <a href="https://github.com/Aircoookie/WLED/blob/master/LICENSE" target="_blank">MIT license</a></i><br><br>
|
||||
Server message: <span class="sip"> Response error! </span><hr>
|
||||
<button type="button" onclick="B()">Back</button><button type="submit">Save & Reboot</button>
|
||||
</form>
|
||||
|
|
|
@ -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><br>
|
||||
A huge thank you to everyone who helped me create WLED!<br><br>
|
||||
(c) 2016-2019 Christian Schwinne<br><i>Licensed under the MIT license</i><br>
|
||||
(c) 2016-2020 Christian Schwinne<br><i>Licensed under the <a href="https://github.com/Aircoookie/WLED/blob/master/LICENSE" target="_blank">MIT license</a></i><br>
|
||||
<br>Server message: <span class="sip">Response error!</span><hr><button
|
||||
type="button" onclick="B()">Back</button><button type="submit">Save & Reboot
|
||||
</button></form></body></html>)=====";
|
||||
|
|
|
@ -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++)
|
||||
{
|
||||
|
|
|
@ -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<JsonObject>());
|
||||
} else { //HTTP API
|
||||
String apireq = "win&";
|
||||
apireq += (char*)payload;
|
||||
handleSet(nullptr, apireq);
|
||||
}
|
||||
} else if (strcmp(topic, "") == 0)
|
||||
{
|
||||
parseMQTTBriPayload(payload);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue