WiZ Lights usermod - Adding more options and features (#2638)
* Update wizlights.h adds new features and options for wizlights usermod * Update wizlights.h Change how IPs are numbered. Non-programmers incorrectly start counting at 1 * Update wizlights.h updated default cold white enhanced white setting to a lower value. * Update wizlights.h added logic for connection check before UDP sending. Seems more important for ESP32 * Update readme.md
This commit is contained in:
parent
23d39e5366
commit
099d2fd03d
@ -6,8 +6,30 @@ The mod takes the colors from the first few pixels and sends them to the lights.
|
|||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
First, enter how often the data will be sent to the lights (in ms).
|
- Interval (ms)
|
||||||
|
- How frequently to update the WiZ lights, in milliseconds.
|
||||||
Then enter the IPs for the lights to be controlled, in order. There is currently a limit of 10 devices that can be controled, but that number
|
- Setting too low may causse ESP to become unresponsive.
|
||||||
|
- Send Delay (ms)
|
||||||
|
- An optional millisecond delay after updating each WiZ light.
|
||||||
|
- Can help smooth out effects when using a larger number of WiZ lights
|
||||||
|
- Use Enhanced White
|
||||||
|
- Enables using the WiZ lights onboard white LEDs instead of sending maximum RGB values.
|
||||||
|
- Tunable with warm and cool LEDs as supported by WiZ bulbs
|
||||||
|
- Note: Only sent when max RGB value is set, need to have automatic brightness limiter disabled
|
||||||
|
- ToDo: Have better logic for white value mixing to better take advantage of the lights capabilities
|
||||||
|
- Always Force Update
|
||||||
|
- Can be enabled to always send update message to light, even when color matches what was previously sent.
|
||||||
|
- Force update every x minutes
|
||||||
|
- Configuration option to allow adjusting the default force update timeout of 5 minutes.
|
||||||
|
- Setting to 0 has the same impact as enabling Always Force Update
|
||||||
|
-
|
||||||
|
Then enter the IPs for the lights to be controlled, in order. There is currently a limit of 15 devices that can be controled, but that number
|
||||||
can be easily changed by updating _MAX_WIZ_LIGHTS_.
|
can be easily changed by updating _MAX_WIZ_LIGHTS_.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Related project
|
||||||
|
|
||||||
|
If you use these lights and python, make sure to check out the [pywizlight](https://github.com/sbidy/pywizlight) project. I learned how to
|
||||||
|
format the messages to control the lights from that project.
|
||||||
|
@ -4,117 +4,134 @@
|
|||||||
#include <WiFiUdp.h>
|
#include <WiFiUdp.h>
|
||||||
|
|
||||||
// Maximum number of lights supported
|
// Maximum number of lights supported
|
||||||
#define MAX_WIZ_LIGHTS 10
|
#define MAX_WIZ_LIGHTS 15
|
||||||
|
|
||||||
// UDP object, to send messages
|
|
||||||
WiFiUDP UDP;
|
WiFiUDP UDP;
|
||||||
|
|
||||||
// Function to send a color to a light
|
|
||||||
void sendColor(IPAddress ip, uint32_t color) {
|
|
||||||
UDP.beginPacket(ip, 38899);
|
|
||||||
if (color == 0) {
|
|
||||||
UDP.print("{\"method\":\"setPilot\",\"params\":{\"state\":false}}");
|
|
||||||
} else {
|
|
||||||
UDP.print("{\"method\":\"setPilot\",\"params\":{\"state\":true, \"r\":");
|
|
||||||
UDP.print(R(color));
|
|
||||||
UDP.print(",\"g\":");
|
|
||||||
UDP.print(G(color));
|
|
||||||
UDP.print(",\"b\":");
|
|
||||||
UDP.print(B(color));
|
|
||||||
UDP.print("}}");
|
|
||||||
}
|
|
||||||
UDP.endPacket();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create label for the usermode page (I cannot make it work with JSON arrays...)
|
|
||||||
String getJsonLabel(uint8_t i) {
|
|
||||||
return "ip_light_" + String(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
class WizLightsUsermod : public Usermod {
|
class WizLightsUsermod : public Usermod {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Keep track of the last time the lights were updated
|
|
||||||
unsigned long lastTime = 0;
|
unsigned long lastTime = 0;
|
||||||
|
|
||||||
// Specify how often WLED sends data to the Wiz lights
|
|
||||||
long updateInterval;
|
long updateInterval;
|
||||||
|
long sendDelay;
|
||||||
|
|
||||||
// Save the IP of the lights
|
long forceUpdateMinutes;
|
||||||
IPAddress lightsIP[MAX_WIZ_LIGHTS];
|
bool forceUpdate;
|
||||||
bool lightsValid[MAX_WIZ_LIGHTS];
|
|
||||||
|
bool useEnhancedWhite;
|
||||||
|
long warmWhite;
|
||||||
|
long coldWhite;
|
||||||
|
|
||||||
|
IPAddress lightsIP[MAX_WIZ_LIGHTS]; // Stores Light IP addresses
|
||||||
|
bool lightsValid[MAX_WIZ_LIGHTS]; // Stores Light IP address validity
|
||||||
|
uint32_t colorsSent[MAX_WIZ_LIGHTS]; // Stores last color sent for each light
|
||||||
|
|
||||||
|
|
||||||
// Variable that keeps track of RBG values for the lights
|
|
||||||
uint32_t colorsSent[MAX_WIZ_LIGHTS];
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
//Functions called by WLED
|
|
||||||
|
|
||||||
/*
|
|
||||||
* loop() is called continuously. Here you can check for events, read sensors, etc.
|
|
||||||
*/
|
|
||||||
void loop() {
|
|
||||||
// Calculate how long since the last update
|
|
||||||
unsigned long ellapsedTime = millis() - lastTime;
|
|
||||||
|
|
||||||
if (ellapsedTime > updateInterval) {
|
|
||||||
// Keep track of whether we are updating any of the lights
|
|
||||||
bool update = false;
|
|
||||||
|
|
||||||
// Loop through the lights
|
// Send JSON blob to WiZ Light over UDP
|
||||||
for (uint8_t i = 0; i < MAX_WIZ_LIGHTS; i++) {
|
// RGB or C/W white
|
||||||
// Check if we have a valid IP
|
// TODO:
|
||||||
if (!lightsValid[i]) { continue; }
|
// Better utilize WLED existing white mixing logic
|
||||||
|
void wizSendColor(IPAddress ip, uint32_t color) {
|
||||||
|
UDP.beginPacket(ip, 38899);
|
||||||
|
|
||||||
// Get the first colors in the strip
|
// If no LED color, turn light off. Note wiz light setting for "Off fade-out" will be applied by the light itself.
|
||||||
uint32_t new_color = strip.getPixelColor(i);
|
if (color == 0) {
|
||||||
|
UDP.print("{\"method\":\"setPilot\",\"params\":{\"state\":false}}");
|
||||||
|
|
||||||
// Check if the color has changed from the last one sent
|
// If color is WHITE, try and use the lights WHITE LEDs instead of mixing RGB LEDs
|
||||||
// Force an update every 5 minutes, in case the colors don't change
|
} else if (color == 16777215 && useEnhancedWhite){
|
||||||
// (the lights could have been reset by turning off and on)
|
|
||||||
if ((new_color != colorsSent[i]) | (ellapsedTime > 5*60000)) {
|
// set cold white light only
|
||||||
// It has changed, send the new color to the light
|
if (coldWhite > 0 && warmWhite == 0){
|
||||||
update = true;
|
UDP.print("{\"method\":\"setPilot\",\"params\":{\"c\":"); UDP.print(coldWhite) ;UDP.print("}}");}
|
||||||
sendColor(lightsIP[i], new_color);
|
|
||||||
colorsSent[i] = new_color;
|
// set warm white light only
|
||||||
}
|
if (warmWhite > 0 && coldWhite == 0){
|
||||||
}
|
UDP.print("{\"method\":\"setPilot\",\"params\":{\"w\":"); UDP.print(warmWhite) ;UDP.print("}}");}
|
||||||
|
|
||||||
// We sent an update, wait until we do this again
|
// set combination of warm and cold white light
|
||||||
if (update) {
|
if (coldWhite > 0 && warmWhite > 0){
|
||||||
lastTime = millis();
|
UDP.print("{\"method\":\"setPilot\",\"params\":{\"c\":"); UDP.print(coldWhite) ;UDP.print(",\"w\":"); UDP.print(warmWhite); UDP.print("}}");}
|
||||||
}
|
|
||||||
|
// Send color as RGB
|
||||||
|
} else {
|
||||||
|
UDP.print("{\"method\":\"setPilot\",\"params\":{\"r\":");
|
||||||
|
UDP.print(R(color));
|
||||||
|
UDP.print(",\"g\":");
|
||||||
|
UDP.print(G(color));
|
||||||
|
UDP.print(",\"b\":");
|
||||||
|
UDP.print(B(color));
|
||||||
|
UDP.print("}}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UDP.endPacket();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* addToConfig() can be used to add custom persistent settings to the cfg.json file in the "um" (usermod) object.
|
|
||||||
* It will be called by WLED when settings are actually saved (for example, LED settings are saved)
|
// TODO: Check millis() rollover
|
||||||
* If you want to force saving the current state, use serializeConfig() in your loop().
|
void loop() {
|
||||||
*/
|
|
||||||
|
// Make sure we are connected first
|
||||||
|
if (!WLED_CONNECTED) return;
|
||||||
|
|
||||||
|
unsigned long ellapsedTime = millis() - lastTime;
|
||||||
|
if (ellapsedTime > updateInterval) {
|
||||||
|
bool update = false;
|
||||||
|
for (uint8_t i = 0; i < MAX_WIZ_LIGHTS; i++) {
|
||||||
|
if (!lightsValid[i]) { continue; }
|
||||||
|
uint32_t newColor = strip.getPixelColor(i);
|
||||||
|
if (forceUpdate || (newColor != colorsSent[i]) || (ellapsedTime > forceUpdateMinutes*60000)){
|
||||||
|
wizSendColor(lightsIP[i], newColor);
|
||||||
|
colorsSent[i] = newColor;
|
||||||
|
update = true;
|
||||||
|
delay(sendDelay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (update) lastTime = millis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void addToConfig(JsonObject& root)
|
void addToConfig(JsonObject& root)
|
||||||
{
|
{
|
||||||
JsonObject top = root.createNestedObject("wizLightsUsermod");
|
JsonObject top = root.createNestedObject("wizLightsUsermod");
|
||||||
top["interval_ms"] = updateInterval;
|
top["Interval (ms)"] = updateInterval;
|
||||||
|
top["Send Delay (ms)"] = sendDelay;
|
||||||
|
top["Use Enhanced White *"] = useEnhancedWhite;
|
||||||
|
top["* Warm White Value (0-255)"] = warmWhite;
|
||||||
|
top["* Cold White Value (0-255)"] = coldWhite;
|
||||||
|
top["Always Force Update"] = forceUpdate;
|
||||||
|
top["Force Update Every x Minutes"] = forceUpdateMinutes;
|
||||||
|
|
||||||
for (uint8_t i = 0; i < MAX_WIZ_LIGHTS; i++) {
|
for (uint8_t i = 0; i < MAX_WIZ_LIGHTS; i++) {
|
||||||
top[getJsonLabel(i)] = lightsIP[i].toString();
|
top[getJsonLabel(i)] = lightsIP[i].toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* readFromConfig() can be used to read back the custom settings you added with addToConfig().
|
|
||||||
* This is called by WLED when settings are loaded (currently this only happens immediately after boot, or after saving on the Usermod Settings page)
|
|
||||||
*/
|
|
||||||
bool readFromConfig(JsonObject& root)
|
bool readFromConfig(JsonObject& root)
|
||||||
{
|
{
|
||||||
// default settings values could be set here (or below using the 3-argument getJsonValue()) instead of in the class definition or constructor
|
|
||||||
// setting them inside readFromConfig() is slightly more robust, handling the rare but plausible use case of single value being missing after boot (e.g. if the cfg.json was manually edited and a value was removed)
|
|
||||||
|
|
||||||
JsonObject top = root["wizLightsUsermod"];
|
JsonObject top = root["wizLightsUsermod"];
|
||||||
|
|
||||||
bool configComplete = !top.isNull();
|
bool configComplete = !top.isNull();
|
||||||
|
|
||||||
// Read interval to update the lights
|
configComplete &= getJsonValue(top["Interval (ms)"], updateInterval, 1000); // How frequently to update the wiz lights
|
||||||
configComplete &= getJsonValue(top["interval_ms"], updateInterval, 1000);
|
configComplete &= getJsonValue(top["Send Delay (ms)"], sendDelay, 0); // Optional delay after sending each UDP message
|
||||||
|
configComplete &= getJsonValue(top["Use Enhanced White *"], useEnhancedWhite, false); // When color is white use wiz white LEDs instead of mixing RGB
|
||||||
|
configComplete &= getJsonValue(top["* Warm White Value (0-255)"], warmWhite, 0); // Warm White LED value for Enhanced White
|
||||||
|
configComplete &= getJsonValue(top["* Cold White Value (0-255)"], coldWhite, 50); // Cold White LED value for Enhanced White
|
||||||
|
configComplete &= getJsonValue(top["Always Force Update"], forceUpdate, false); // Update wiz light every loop, even if color value has not changed
|
||||||
|
configComplete &= getJsonValue(top["Force Update Every x Minutes"], forceUpdateMinutes, 5); // Update wiz light if color value has not changed, every x minutes
|
||||||
|
|
||||||
// Read list of IPs
|
// Read list of IPs
|
||||||
String tempIp;
|
String tempIp;
|
||||||
@ -123,20 +140,15 @@ class WizLightsUsermod : public Usermod {
|
|||||||
lightsValid[i] = lightsIP[i].fromString(tempIp);
|
lightsValid[i] = lightsIP[i].fromString(tempIp);
|
||||||
|
|
||||||
// If the IP is not valid, force the value to be empty
|
// If the IP is not valid, force the value to be empty
|
||||||
if (!lightsValid[i]) {
|
if (!lightsValid[i]){lightsIP[i].fromString("0.0.0.0");}
|
||||||
lightsIP[i].fromString("0.0.0.0");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return configComplete;
|
return configComplete;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!).
|
// Create label for the usermod page (I cannot make it work with JSON arrays...)
|
||||||
* This could be used in the future for the system to determine whether your usermod is installed.
|
String getJsonLabel(uint8_t i) {return "WiZ Light IP #" + String(i+1);}
|
||||||
*/
|
|
||||||
uint16_t getId()
|
uint16_t getId(){return USERMOD_ID_WIZLIGHTS;}
|
||||||
{
|
};
|
||||||
return USERMOD_ID_WIZLIGHTS;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
Loading…
Reference in New Issue
Block a user