diff --git a/usermods/wizlights/readme.md b/usermods/wizlights/readme.md new file mode 100644 index 00000000..271c3076 --- /dev/null +++ b/usermods/wizlights/readme.md @@ -0,0 +1,13 @@ +# Controlling Wiz lights + +This usermod allows the control of [WiZ](https://www.wizconnected.com/en/consumer/) lights that are in the same network as the WLED controller. + +The mod takes the colors from the first few pixels and sends them to the lights. + +## Configuration + +First, enter how often the data will be sent to the lights (in ms). + +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 +can be easily changed by updating _MAX_WIZ_LIGHTS_. + diff --git a/usermods/wizlights/wizlights.h b/usermods/wizlights/wizlights.h new file mode 100644 index 00000000..230a7a8a --- /dev/null +++ b/usermods/wizlights/wizlights.h @@ -0,0 +1,142 @@ +#pragma once + +#include "wled.h" +#include + +// Maximum number of lights supported +#define MAX_WIZ_LIGHTS 10 + +// UDP object, to send messages +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 { + private: + // Keep track of the last time the lights were updated + unsigned long lastTime = 0; + + // Specify how often WLED sends data to the Wiz lights + long updateInterval; + + // Save the IP of the lights + IPAddress lightsIP[MAX_WIZ_LIGHTS]; + bool lightsValid[MAX_WIZ_LIGHTS]; + + // Variable that keeps track of RBG values for the lights + uint32_t colorsSent[MAX_WIZ_LIGHTS]; + + 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 + for (uint8_t i = 0; i < MAX_WIZ_LIGHTS; i++) { + // Check if we have a valid IP + if (!lightsValid[i]) { continue; } + + // Get the first colors in the strip + uint32_t new_color = strip.getPixelColor(i); + + // Check if the color has changed from the last one sent + // Force an update every 5 minutes, in case the colors don't change + // (the lights could have been reset by turning off and on) + if ((new_color != colorsSent[i]) | (ellapsedTime > 5*60000)) { + // It has changed, send the new color to the light + update = true; + sendColor(lightsIP[i], new_color); + colorsSent[i] = new_color; + } + } + + // We sent an update, wait until we do this again + if (update) { + lastTime = millis(); + } + } + } + + /* + * 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) + * If you want to force saving the current state, use serializeConfig() in your loop(). + */ + void addToConfig(JsonObject& root) + { + JsonObject top = root.createNestedObject("wizLightsUsermod"); + top["interval_ms"] = updateInterval; + for (uint8_t i = 0; i < MAX_WIZ_LIGHTS; i++) { + 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) + { + // 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"]; + + bool configComplete = !top.isNull(); + + // Read interval to update the lights + configComplete &= getJsonValue(top["interval_ms"], updateInterval, 1000); + + // Read list of IPs + String tempIp; + for (uint8_t i = 0; i < MAX_WIZ_LIGHTS; i++) { + configComplete &= getJsonValue(top[getJsonLabel(i)], tempIp, "0.0.0.0"); + lightsValid[i] = lightsIP[i].fromString(tempIp); + + // If the IP is not valid, force the value to be empty + if (!lightsValid[i]) { + lightsIP[i].fromString("0.0.0.0"); + } + } + + return configComplete; + } + + /* + * getId() allows you to optionally give your V2 usermod an unique ID (please define it in const.h!). + * This could be used in the future for the system to determine whether your usermod is installed. + */ + uint16_t getId() + { + return USERMOD_ID_WIZLIGHTS; + } +}; \ No newline at end of file diff --git a/wled00/const.h b/wled00/const.h index fcdf83ee..17326c70 100644 --- a/wled00/const.h +++ b/wled00/const.h @@ -72,6 +72,7 @@ #define USERMOD_ID_QUINLED_AN_PENTA 23 //Usermod "quinled-an-penta.h" #define USERMOD_ID_SSDR 24 //Usermod "usermod_v2_seven_segment_display_reloaded.h" #define USERMOD_ID_CRONIXIE 25 //Usermod "usermod_cronixie.h" +#define USERMOD_ID_WIZLIGHTS 26 //Usermod "wizlights.h" //Access point behavior #define AP_BEHAVIOR_BOOT_NO_CONN 0 //Open AP when no connection after boot diff --git a/wled00/usermods_list.cpp b/wled00/usermods_list.cpp index 82ac6af9..caf8707d 100644 --- a/wled00/usermods_list.cpp +++ b/wled00/usermods_list.cpp @@ -112,6 +112,10 @@ #include "../usermods/quinled-an-penta/quinled-an-penta.h" #endif +#ifdef USERMOD_WIZLIGHTS +#include "../usermods/wizlights/wizlights.h" +#endif + void registerUsermods() { /* @@ -211,4 +215,8 @@ void registerUsermods() #ifdef QUINLED_AN_PENTA usermods.add(new QuinLEDAnPentaUsermod()); #endif + + #ifdef USERMOD_WIZLIGHTS + usermods.add(new WizLightsUsermod()); + #endif }