├── ESP MQTT Digital LEDs Wiring Diagram.fzz ├── ESP MQTT Digital LEDs Wiring Diagram.png ├── ESP_MQTT_Digital_LEDs └── ESP_MQTT_Digital_LEDs.ino ├── Example Home Assistant Configuration.yaml ├── LICENSE └── README.md /ESP MQTT Digital LEDs Wiring Diagram.fzz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bruhautomation/ESP-MQTT-JSON-Digital-LEDs/1d7c0c30d4eaa6dca37226a31cb2da40458fd1b8/ESP MQTT Digital LEDs Wiring Diagram.fzz -------------------------------------------------------------------------------- /ESP MQTT Digital LEDs Wiring Diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bruhautomation/ESP-MQTT-JSON-Digital-LEDs/1d7c0c30d4eaa6dca37226a31cb2da40458fd1b8/ESP MQTT Digital LEDs Wiring Diagram.png -------------------------------------------------------------------------------- /ESP_MQTT_Digital_LEDs/ESP_MQTT_Digital_LEDs.ino: -------------------------------------------------------------------------------- 1 | /* 2 | .______ .______ __ __ __ __ ___ __ __ .___________. ______ .___ ___. ___ .___________. __ ______ .__ __. 3 | | _ \ | _ \ | | | | | | | | / \ | | | | | | / __ \ | \/ | / \ | || | / __ \ | \ | | 4 | | |_) | | |_) | | | | | | |__| | / ^ \ | | | | `---| |----`| | | | | \ / | / ^ \ `---| |----`| | | | | | | \| | 5 | | _ < | / | | | | | __ | / /_\ \ | | | | | | | | | | | |\/| | / /_\ \ | | | | | | | | | . ` | 6 | | |_) | | |\ \-.| `--' | | | | | / _____ \ | `--' | | | | `--' | | | | | / _____ \ | | | | | `--' | | |\ | 7 | |______/ | _| `.__| \______/ |__| |__| /__/ \__\ \______/ |__| \______/ |__| |__| /__/ \__\ |__| |__| \______/ |__| \__| 8 | 9 | Thanks much to @corbanmailloux for providing a great framework for implementing flash/fade with HomeAssistant https://github.com/corbanmailloux/esp-mqtt-rgb-led 10 | 11 | To use this code you will need the following dependancies: 12 | 13 | - Support for the ESP8266 boards. 14 | - You can add it to the board manager by going to File -> Preference and pasting http://arduino.esp8266.com/stable/package_esp8266com_index.json into the Additional Board Managers URL field. 15 | - Next, download the ESP8266 dependancies by going to Tools -> Board -> Board Manager and searching for ESP8266 and installing it. 16 | 17 | - You will also need to download the follow libraries by going to Sketch -> Include Libraries -> Manage Libraries 18 | - FastLED 19 | - PubSubClient 20 | - ArduinoJSON 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include "FastLED.h" 27 | #include 28 | #include 29 | #include 30 | 31 | 32 | 33 | /************ WIFI and MQTT Information (CHANGE THESE FOR YOUR SETUP) ******************/ 34 | const char* ssid = "YourSSID"; //type your WIFI information inside the quotes 35 | const char* password = "YourWIFIpassword"; 36 | const char* mqtt_server = "your.MQTT.server.ip"; 37 | const char* mqtt_username = "yourMQTTusername"; 38 | const char* mqtt_password = "yourMQTTpassword"; 39 | const int mqtt_port = 1883; 40 | 41 | 42 | 43 | /**************************** FOR OTA **************************************************/ 44 | #define SENSORNAME "porch" //change this to whatever you want to call your device 45 | #define OTApassword "yourOTApassword" //the password you will need to enter to upload remotely via the ArduinoIDE 46 | int OTAport = 8266; 47 | 48 | 49 | 50 | /************* MQTT TOPICS (change these topics as you wish) **************************/ 51 | const char* light_state_topic = "bruh/porch"; 52 | const char* light_set_topic = "bruh/porch/set"; 53 | 54 | const char* on_cmd = "ON"; 55 | const char* off_cmd = "OFF"; 56 | const char* effect = "solid"; 57 | String effectString = "solid"; 58 | String oldeffectString = "solid"; 59 | 60 | 61 | 62 | /****************************************FOR JSON***************************************/ 63 | const int BUFFER_SIZE = JSON_OBJECT_SIZE(10); 64 | #define MQTT_MAX_PACKET_SIZE 512 65 | 66 | 67 | 68 | /*********************************** FastLED Defintions ********************************/ 69 | #define NUM_LEDS 186 70 | #define DATA_PIN 5 71 | //#define CLOCK_PIN 5 72 | #define CHIPSET WS2811 73 | #define COLOR_ORDER BRG 74 | 75 | byte realRed = 0; 76 | byte realGreen = 0; 77 | byte realBlue = 0; 78 | 79 | byte red = 255; 80 | byte green = 255; 81 | byte blue = 255; 82 | byte brightness = 255; 83 | 84 | 85 | 86 | /******************************** GLOBALS for fade/flash *******************************/ 87 | bool stateOn = false; 88 | bool startFade = false; 89 | bool onbeforeflash = false; 90 | unsigned long lastLoop = 0; 91 | int transitionTime = 0; 92 | int effectSpeed = 0; 93 | bool inFade = false; 94 | int loopCount = 0; 95 | int stepR, stepG, stepB; 96 | int redVal, grnVal, bluVal; 97 | 98 | bool flash = false; 99 | bool startFlash = false; 100 | int flashLength = 0; 101 | unsigned long flashStartTime = 0; 102 | byte flashRed = red; 103 | byte flashGreen = green; 104 | byte flashBlue = blue; 105 | byte flashBrightness = brightness; 106 | 107 | 108 | 109 | /********************************** GLOBALS for EFFECTS ******************************/ 110 | //RAINBOW 111 | uint8_t thishue = 0; // Starting hue value. 112 | uint8_t deltahue = 10; 113 | 114 | //CANDYCANE 115 | CRGBPalette16 currentPalettestriped; //for Candy Cane 116 | CRGBPalette16 gPal; //for fire 117 | 118 | //NOISE 119 | static uint16_t dist; // A random number for our noise generator. 120 | uint16_t scale = 30; // Wouldn't recommend changing this on the fly, or the animation will be really blocky. 121 | uint8_t maxChanges = 48; // Value for blending between palettes. 122 | CRGBPalette16 targetPalette(OceanColors_p); 123 | CRGBPalette16 currentPalette(CRGB::Black); 124 | 125 | //TWINKLE 126 | #define DENSITY 80 127 | int twinklecounter = 0; 128 | 129 | //RIPPLE 130 | uint8_t colour; // Ripple colour is randomized. 131 | int center = 0; // Center of the current ripple. 132 | int step = -1; // -1 is the initializing step. 133 | uint8_t myfade = 255; // Starting brightness. 134 | #define maxsteps 16 // Case statement wouldn't allow a variable. 135 | uint8_t bgcol = 0; // Background colour rotates. 136 | int thisdelay = 20; // Standard delay value. 137 | 138 | //DOTS 139 | uint8_t count = 0; // Count up to 255 and then reverts to 0 140 | uint8_t fadeval = 224; // Trail behind the LED's. Lower => faster fade. 141 | uint8_t bpm = 30; 142 | 143 | //LIGHTNING 144 | uint8_t frequency = 50; // controls the interval between strikes 145 | uint8_t flashes = 8; //the upper limit of flashes per strike 146 | unsigned int dimmer = 1; 147 | uint8_t ledstart; // Starting location of a flash 148 | uint8_t ledlen; 149 | int lightningcounter = 0; 150 | 151 | //FUNKBOX 152 | int idex = 0; //-LED INDEX (0 to NUM_LEDS-1 153 | int TOP_INDEX = int(NUM_LEDS / 2); 154 | int thissat = 255; //-FX LOOPS DELAY VAR 155 | uint8_t thishuepolice = 0; 156 | int antipodal_index(int i) { 157 | int iN = i + TOP_INDEX; 158 | if (i >= TOP_INDEX) { 159 | iN = ( i + TOP_INDEX ) % NUM_LEDS; 160 | } 161 | return iN; 162 | } 163 | 164 | //FIRE 165 | #define COOLING 55 166 | #define SPARKING 120 167 | bool gReverseDirection = false; 168 | 169 | //BPM 170 | uint8_t gHue = 0; 171 | 172 | 173 | WiFiClient espClient; 174 | PubSubClient client(espClient); 175 | struct CRGB leds[NUM_LEDS]; 176 | 177 | 178 | 179 | /********************************** START SETUP*****************************************/ 180 | void setup() { 181 | Serial.begin(115200); 182 | FastLED.addLeds(leds, NUM_LEDS); 183 | 184 | setupStripedPalette( CRGB::Red, CRGB::Red, CRGB::White, CRGB::White); //for CANDY CANE 185 | gPal = HeatColors_p; //for FIRE 186 | 187 | setup_wifi(); 188 | client.setServer(mqtt_server, mqtt_port); 189 | client.setCallback(callback); 190 | 191 | //OTA SETUP 192 | ArduinoOTA.setPort(OTAport); 193 | // Hostname defaults to esp8266-[ChipID] 194 | ArduinoOTA.setHostname(SENSORNAME); 195 | 196 | // No authentication by default 197 | ArduinoOTA.setPassword((const char *)OTApassword); 198 | 199 | ArduinoOTA.onStart([]() { 200 | Serial.println("Starting"); 201 | }); 202 | ArduinoOTA.onEnd([]() { 203 | Serial.println("\nEnd"); 204 | }); 205 | ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) { 206 | Serial.printf("Progress: %u%%\r", (progress / (total / 100))); 207 | }); 208 | ArduinoOTA.onError([](ota_error_t error) { 209 | Serial.printf("Error[%u]: ", error); 210 | if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed"); 211 | else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed"); 212 | else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed"); 213 | else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed"); 214 | else if (error == OTA_END_ERROR) Serial.println("End Failed"); 215 | }); 216 | ArduinoOTA.begin(); 217 | 218 | Serial.println("Ready"); 219 | Serial.print("IP Address: "); 220 | Serial.println(WiFi.localIP()); 221 | 222 | } 223 | 224 | 225 | 226 | 227 | /********************************** START SETUP WIFI*****************************************/ 228 | void setup_wifi() { 229 | 230 | delay(10); 231 | // We start by connecting to a WiFi network 232 | Serial.println(); 233 | Serial.print("Connecting to "); 234 | Serial.println(ssid); 235 | 236 | WiFi.mode(WIFI_STA); 237 | WiFi.begin(ssid, password); 238 | 239 | while (WiFi.status() != WL_CONNECTED) { 240 | delay(500); 241 | Serial.print("."); 242 | } 243 | 244 | Serial.println(""); 245 | Serial.println("WiFi connected"); 246 | Serial.println("IP address: "); 247 | Serial.println(WiFi.localIP()); 248 | } 249 | 250 | /* 251 | SAMPLE PAYLOAD: 252 | { 253 | "brightness": 120, 254 | "color": { 255 | "r": 255, 256 | "g": 100, 257 | "b": 100 258 | }, 259 | "flash": 2, 260 | "transition": 5, 261 | "state": "ON" 262 | } 263 | */ 264 | 265 | 266 | 267 | /********************************** START CALLBACK*****************************************/ 268 | void callback(char* topic, byte* payload, unsigned int length) { 269 | Serial.print("Message arrived ["); 270 | Serial.print(topic); 271 | Serial.print("] "); 272 | 273 | char message[length + 1]; 274 | for (int i = 0; i < length; i++) { 275 | message[i] = (char)payload[i]; 276 | } 277 | message[length] = '\0'; 278 | Serial.println(message); 279 | 280 | if (!processJson(message)) { 281 | return; 282 | } 283 | 284 | if (stateOn) { 285 | 286 | realRed = map(red, 0, 255, 0, brightness); 287 | realGreen = map(green, 0, 255, 0, brightness); 288 | realBlue = map(blue, 0, 255, 0, brightness); 289 | } 290 | else { 291 | 292 | realRed = 0; 293 | realGreen = 0; 294 | realBlue = 0; 295 | } 296 | 297 | Serial.println(effect); 298 | 299 | startFade = true; 300 | inFade = false; // Kill the current fade 301 | 302 | sendState(); 303 | } 304 | 305 | 306 | 307 | /********************************** START PROCESS JSON*****************************************/ 308 | bool processJson(char* message) { 309 | StaticJsonBuffer jsonBuffer; 310 | 311 | JsonObject& root = jsonBuffer.parseObject(message); 312 | 313 | if (!root.success()) { 314 | Serial.println("parseObject() failed"); 315 | return false; 316 | } 317 | 318 | if (root.containsKey("state")) { 319 | if (strcmp(root["state"], on_cmd) == 0) { 320 | stateOn = true; 321 | } 322 | else if (strcmp(root["state"], off_cmd) == 0) { 323 | stateOn = false; 324 | onbeforeflash = false; 325 | } 326 | } 327 | 328 | // If "flash" is included, treat RGB and brightness differently 329 | if (root.containsKey("flash")) { 330 | flashLength = (int)root["flash"] * 1000; 331 | 332 | oldeffectString = effectString; 333 | 334 | if (root.containsKey("brightness")) { 335 | flashBrightness = root["brightness"]; 336 | } 337 | else { 338 | flashBrightness = brightness; 339 | } 340 | 341 | if (root.containsKey("color")) { 342 | flashRed = root["color"]["r"]; 343 | flashGreen = root["color"]["g"]; 344 | flashBlue = root["color"]["b"]; 345 | } 346 | else { 347 | flashRed = red; 348 | flashGreen = green; 349 | flashBlue = blue; 350 | } 351 | 352 | if (root.containsKey("effect")) { 353 | effect = root["effect"]; 354 | effectString = effect; 355 | twinklecounter = 0; //manage twinklecounter 356 | } 357 | 358 | if (root.containsKey("transition")) { 359 | transitionTime = root["transition"]; 360 | } 361 | else if ( effectString == "solid") { 362 | transitionTime = 0; 363 | } 364 | 365 | flashRed = map(flashRed, 0, 255, 0, flashBrightness); 366 | flashGreen = map(flashGreen, 0, 255, 0, flashBrightness); 367 | flashBlue = map(flashBlue, 0, 255, 0, flashBrightness); 368 | 369 | flash = true; 370 | startFlash = true; 371 | } 372 | else { // Not flashing 373 | flash = false; 374 | 375 | if (stateOn) { //if the light is turned on and the light isn't flashing 376 | onbeforeflash = true; 377 | } 378 | 379 | if (root.containsKey("color")) { 380 | red = root["color"]["r"]; 381 | green = root["color"]["g"]; 382 | blue = root["color"]["b"]; 383 | } 384 | 385 | if (root.containsKey("color_temp")) { 386 | //temp comes in as mireds, need to convert to kelvin then to RGB 387 | int color_temp = root["color_temp"]; 388 | unsigned int kelvin = MILLION / color_temp; 389 | 390 | temp2rgb(kelvin); 391 | 392 | } 393 | 394 | if (root.containsKey("brightness")) { 395 | brightness = root["brightness"]; 396 | } 397 | 398 | if (root.containsKey("effect")) { 399 | effect = root["effect"]; 400 | effectString = effect; 401 | twinklecounter = 0; //manage twinklecounter 402 | } 403 | 404 | if (root.containsKey("transition")) { 405 | transitionTime = root["transition"]; 406 | } 407 | else if ( effectString == "solid") { 408 | transitionTime = 0; 409 | } 410 | 411 | } 412 | 413 | return true; 414 | } 415 | 416 | 417 | 418 | /********************************** START SEND STATE*****************************************/ 419 | void sendState() { 420 | StaticJsonBuffer jsonBuffer; 421 | 422 | JsonObject& root = jsonBuffer.createObject(); 423 | 424 | root["state"] = (stateOn) ? on_cmd : off_cmd; 425 | JsonObject& color = root.createNestedObject("color"); 426 | color["r"] = red; 427 | color["g"] = green; 428 | color["b"] = blue; 429 | 430 | root["brightness"] = brightness; 431 | root["effect"] = effectString.c_str(); 432 | 433 | 434 | char buffer[root.measureLength() + 1]; 435 | root.printTo(buffer, sizeof(buffer)); 436 | 437 | client.publish(light_state_topic, buffer, true); 438 | } 439 | 440 | 441 | 442 | /********************************** START RECONNECT*****************************************/ 443 | void reconnect() { 444 | // Loop until we're reconnected 445 | while (!client.connected()) { 446 | Serial.print("Attempting MQTT connection..."); 447 | // Attempt to connect 448 | if (client.connect(SENSORNAME, mqtt_username, mqtt_password)) { 449 | Serial.println("connected"); 450 | client.subscribe(light_set_topic); 451 | setColor(0, 0, 0); 452 | sendState(); 453 | } else { 454 | Serial.print("failed, rc="); 455 | Serial.print(client.state()); 456 | Serial.println(" try again in 5 seconds"); 457 | // Wait 5 seconds before retrying 458 | delay(5000); 459 | } 460 | } 461 | } 462 | 463 | 464 | 465 | /********************************** START Set Color*****************************************/ 466 | void setColor(int inR, int inG, int inB) { 467 | for (int i = 0; i < NUM_LEDS; i++) { 468 | leds[i].red = inR; 469 | leds[i].green = inG; 470 | leds[i].blue = inB; 471 | } 472 | 473 | FastLED.show(); 474 | 475 | Serial.println("Setting LEDs:"); 476 | Serial.print("r: "); 477 | Serial.print(inR); 478 | Serial.print(", g: "); 479 | Serial.print(inG); 480 | Serial.print(", b: "); 481 | Serial.println(inB); 482 | } 483 | 484 | 485 | 486 | /********************************** START MAIN LOOP*****************************************/ 487 | void loop() { 488 | 489 | if (!client.connected()) { 490 | reconnect(); 491 | } 492 | 493 | if (WiFi.status() != WL_CONNECTED) { 494 | delay(1); 495 | Serial.print("WIFI Disconnected. Attempting reconnection."); 496 | setup_wifi(); 497 | return; 498 | } 499 | 500 | 501 | 502 | client.loop(); 503 | 504 | ArduinoOTA.handle(); 505 | 506 | 507 | //EFFECT BPM 508 | if (effectString == "bpm") { 509 | uint8_t BeatsPerMinute = 62; 510 | CRGBPalette16 palette = PartyColors_p; 511 | uint8_t beat = beatsin8( BeatsPerMinute, 64, 255); 512 | for ( int i = 0; i < NUM_LEDS; i++) { //9948 513 | leds[i] = ColorFromPalette(palette, gHue + (i * 2), beat - gHue + (i * 10)); 514 | } 515 | if (transitionTime == 0 or transitionTime == NULL) { 516 | transitionTime = 30; 517 | } 518 | showleds(); 519 | } 520 | 521 | 522 | //EFFECT Candy Cane 523 | if (effectString == "candy cane") { 524 | static uint8_t startIndex = 0; 525 | startIndex = startIndex + 1; /* higher = faster motion */ 526 | fill_palette( leds, NUM_LEDS, 527 | startIndex, 16, /* higher = narrower stripes */ 528 | currentPalettestriped, 255, LINEARBLEND); 529 | if (transitionTime == 0 or transitionTime == NULL) { 530 | transitionTime = 0; 531 | } 532 | showleds(); 533 | } 534 | 535 | 536 | //EFFECT CONFETTI 537 | if (effectString == "confetti" ) { 538 | fadeToBlackBy( leds, NUM_LEDS, 25); 539 | int pos = random16(NUM_LEDS); 540 | leds[pos] += CRGB(realRed + random8(64), realGreen, realBlue); 541 | if (transitionTime == 0 or transitionTime == NULL) { 542 | transitionTime = 30; 543 | } 544 | showleds(); 545 | } 546 | 547 | 548 | //EFFECT CYCLON RAINBOW 549 | if (effectString == "cyclon rainbow") { //Single Dot Down 550 | static uint8_t hue = 0; 551 | // First slide the led in one direction 552 | for (int i = 0; i < NUM_LEDS; i++) { 553 | // Set the i'th led to red 554 | leds[i] = CHSV(hue++, 255, 255); 555 | // Show the leds 556 | showleds(); 557 | // now that we've shown the leds, reset the i'th led to black 558 | // leds[i] = CRGB::Black; 559 | fadeall(); 560 | // Wait a little bit before we loop around and do it again 561 | delay(10); 562 | } 563 | for (int i = (NUM_LEDS) - 1; i >= 0; i--) { 564 | // Set the i'th led to red 565 | leds[i] = CHSV(hue++, 255, 255); 566 | // Show the leds 567 | showleds(); 568 | // now that we've shown the leds, reset the i'th led to black 569 | // leds[i] = CRGB::Black; 570 | fadeall(); 571 | // Wait a little bit before we loop around and do it again 572 | delay(10); 573 | } 574 | } 575 | 576 | 577 | //EFFECT DOTS 578 | if (effectString == "dots") { 579 | uint8_t inner = beatsin8(bpm, NUM_LEDS / 4, NUM_LEDS / 4 * 3); 580 | uint8_t outer = beatsin8(bpm, 0, NUM_LEDS - 1); 581 | uint8_t middle = beatsin8(bpm, NUM_LEDS / 3, NUM_LEDS / 3 * 2); 582 | leds[middle] = CRGB::Purple; 583 | leds[inner] = CRGB::Blue; 584 | leds[outer] = CRGB::Aqua; 585 | nscale8(leds, NUM_LEDS, fadeval); 586 | 587 | if (transitionTime == 0 or transitionTime == NULL) { 588 | transitionTime = 30; 589 | } 590 | showleds(); 591 | } 592 | 593 | 594 | //EFFECT FIRE 595 | if (effectString == "fire") { 596 | Fire2012WithPalette(); 597 | if (transitionTime == 0 or transitionTime == NULL) { 598 | transitionTime = 150; 599 | } 600 | showleds(); 601 | } 602 | 603 | random16_add_entropy( random8()); 604 | 605 | 606 | //EFFECT Glitter 607 | if (effectString == "glitter") { 608 | fadeToBlackBy( leds, NUM_LEDS, 20); 609 | addGlitterColor(80, realRed, realGreen, realBlue); 610 | if (transitionTime == 0 or transitionTime == NULL) { 611 | transitionTime = 30; 612 | } 613 | showleds(); 614 | } 615 | 616 | 617 | //EFFECT JUGGLE 618 | if (effectString == "juggle" ) { // eight colored dots, weaving in and out of sync with each other 619 | fadeToBlackBy(leds, NUM_LEDS, 20); 620 | for (int i = 0; i < 8; i++) { 621 | leds[beatsin16(i + 7, 0, NUM_LEDS - 1 )] |= CRGB(realRed, realGreen, realBlue); 622 | } 623 | if (transitionTime == 0 or transitionTime == NULL) { 624 | transitionTime = 130; 625 | } 626 | showleds(); 627 | } 628 | 629 | 630 | //EFFECT LIGHTNING 631 | if (effectString == "lightning") { 632 | twinklecounter = twinklecounter + 1; //Resets strip if previous animation was running 633 | if (twinklecounter < 2) { 634 | FastLED.clear(); 635 | FastLED.show(); 636 | } 637 | ledstart = random8(NUM_LEDS); // Determine starting location of flash 638 | ledlen = random8(NUM_LEDS - ledstart); // Determine length of flash (not to go beyond NUM_LEDS-1) 639 | for (int flashCounter = 0; flashCounter < random8(3, flashes); flashCounter++) { 640 | if (flashCounter == 0) dimmer = 5; // the brightness of the leader is scaled down by a factor of 5 641 | else dimmer = random8(1, 3); // return strokes are brighter than the leader 642 | fill_solid(leds + ledstart, ledlen, CHSV(255, 0, 255 / dimmer)); 643 | showleds(); // Show a section of LED's 644 | delay(random8(4, 10)); // each flash only lasts 4-10 milliseconds 645 | fill_solid(leds + ledstart, ledlen, CHSV(255, 0, 0)); // Clear the section of LED's 646 | showleds(); 647 | if (flashCounter == 0) delay (130); // longer delay until next flash after the leader 648 | delay(50 + random8(100)); // shorter delay between strokes 649 | } 650 | delay(random8(frequency) * 100); // delay between strikes 651 | if (transitionTime == 0 or transitionTime == NULL) { 652 | transitionTime = 0; 653 | } 654 | showleds(); 655 | } 656 | 657 | 658 | //EFFECT POLICE ALL 659 | if (effectString == "police all") { //POLICE LIGHTS (TWO COLOR SOLID) 660 | idex++; 661 | if (idex >= NUM_LEDS) { 662 | idex = 0; 663 | } 664 | int idexR = idex; 665 | int idexB = antipodal_index(idexR); 666 | int thathue = (thishuepolice + 160) % 255; 667 | leds[idexR] = CHSV(thishuepolice, thissat, 255); 668 | leds[idexB] = CHSV(thathue, thissat, 255); 669 | if (transitionTime == 0 or transitionTime == NULL) { 670 | transitionTime = 30; 671 | } 672 | showleds(); 673 | } 674 | 675 | //EFFECT POLICE ONE 676 | if (effectString == "police one") { 677 | idex++; 678 | if (idex >= NUM_LEDS) { 679 | idex = 0; 680 | } 681 | int idexR = idex; 682 | int idexB = antipodal_index(idexR); 683 | int thathue = (thishuepolice + 160) % 255; 684 | for (int i = 0; i < NUM_LEDS; i++ ) { 685 | if (i == idexR) { 686 | leds[i] = CHSV(thishuepolice, thissat, 255); 687 | } 688 | else if (i == idexB) { 689 | leds[i] = CHSV(thathue, thissat, 255); 690 | } 691 | else { 692 | leds[i] = CHSV(0, 0, 0); 693 | } 694 | } 695 | if (transitionTime == 0 or transitionTime == NULL) { 696 | transitionTime = 30; 697 | } 698 | showleds(); 699 | } 700 | 701 | 702 | //EFFECT RAINBOW 703 | if (effectString == "rainbow") { 704 | // FastLED's built-in rainbow generator 705 | static uint8_t starthue = 0; thishue++; 706 | fill_rainbow(leds, NUM_LEDS, thishue, deltahue); 707 | if (transitionTime == 0 or transitionTime == NULL) { 708 | transitionTime = 130; 709 | } 710 | showleds(); 711 | } 712 | 713 | 714 | //EFFECT RAINBOW WITH GLITTER 715 | if (effectString == "rainbow with glitter") { // FastLED's built-in rainbow generator with Glitter 716 | static uint8_t starthue = 0; 717 | thishue++; 718 | fill_rainbow(leds, NUM_LEDS, thishue, deltahue); 719 | addGlitter(80); 720 | if (transitionTime == 0 or transitionTime == NULL) { 721 | transitionTime = 130; 722 | } 723 | showleds(); 724 | } 725 | 726 | 727 | //EFFECT SIENLON 728 | if (effectString == "sinelon") { 729 | fadeToBlackBy( leds, NUM_LEDS, 20); 730 | int pos = beatsin16(13, 0, NUM_LEDS - 1); 731 | leds[pos] += CRGB(realRed, realGreen, realBlue); 732 | if (transitionTime == 0 or transitionTime == NULL) { 733 | transitionTime = 150; 734 | } 735 | showleds(); 736 | } 737 | 738 | 739 | //EFFECT TWINKLE 740 | if (effectString == "twinkle") { 741 | twinklecounter = twinklecounter + 1; 742 | if (twinklecounter < 2) { //Resets strip if previous animation was running 743 | FastLED.clear(); 744 | FastLED.show(); 745 | } 746 | const CRGB lightcolor(8, 7, 1); 747 | for ( int i = 0; i < NUM_LEDS; i++) { 748 | if ( !leds[i]) continue; // skip black pixels 749 | if ( leds[i].r & 1) { // is red odd? 750 | leds[i] -= lightcolor; // darken if red is odd 751 | } else { 752 | leds[i] += lightcolor; // brighten if red is even 753 | } 754 | } 755 | if ( random8() < DENSITY) { 756 | int j = random16(NUM_LEDS); 757 | if ( !leds[j] ) leds[j] = lightcolor; 758 | } 759 | 760 | if (transitionTime == 0 or transitionTime == NULL) { 761 | transitionTime = 0; 762 | } 763 | showleds(); 764 | } 765 | 766 | 767 | EVERY_N_MILLISECONDS(10) { 768 | 769 | nblendPaletteTowardPalette(currentPalette, targetPalette, maxChanges); // FOR NOISE ANIMATIon 770 | { 771 | gHue++; 772 | } 773 | 774 | //EFFECT NOISE 775 | if (effectString == "noise") { 776 | for (int i = 0; i < NUM_LEDS; i++) { // Just onE loop to fill up the LED array as all of the pixels change. 777 | uint8_t index = inoise8(i * scale, dist + i * scale) % 255; // Get a value from the noise function. I'm using both x and y axis. 778 | leds[i] = ColorFromPalette(currentPalette, index, 255, LINEARBLEND); // With that value, look up the 8 bit colour palette value and assign it to the current LED. 779 | } 780 | dist += beatsin8(10, 1, 4); // Moving along the distance (that random number we started out with). Vary it a bit with a sine wave. 781 | // In some sketches, I've used millis() instead of an incremented counter. Works a treat. 782 | if (transitionTime == 0 or transitionTime == NULL) { 783 | transitionTime = 0; 784 | } 785 | showleds(); 786 | } 787 | 788 | //EFFECT RIPPLE 789 | if (effectString == "ripple") { 790 | for (int i = 0; i < NUM_LEDS; i++) leds[i] = CHSV(bgcol++, 255, 15); // Rotate background colour. 791 | switch (step) { 792 | case -1: // Initialize ripple variables. 793 | center = random(NUM_LEDS); 794 | colour = random8(); 795 | step = 0; 796 | break; 797 | case 0: 798 | leds[center] = CHSV(colour, 255, 255); // Display the first pixel of the ripple. 799 | step ++; 800 | break; 801 | case maxsteps: // At the end of the ripples. 802 | step = -1; 803 | break; 804 | default: // Middle of the ripples. 805 | leds[(center + step + NUM_LEDS) % NUM_LEDS] += CHSV(colour, 255, myfade / step * 2); // Simple wrap from Marc Miller 806 | leds[(center - step + NUM_LEDS) % NUM_LEDS] += CHSV(colour, 255, myfade / step * 2); 807 | step ++; // Next step. 808 | break; 809 | } 810 | if (transitionTime == 0 or transitionTime == NULL) { 811 | transitionTime = 30; 812 | } 813 | showleds(); 814 | } 815 | 816 | } 817 | 818 | 819 | EVERY_N_SECONDS(5) { 820 | targetPalette = CRGBPalette16(CHSV(random8(), 255, random8(128, 255)), CHSV(random8(), 255, random8(128, 255)), CHSV(random8(), 192, random8(128, 255)), CHSV(random8(), 255, random8(128, 255))); 821 | } 822 | 823 | //FLASH AND FADE SUPPORT 824 | if (flash) { 825 | if (startFlash) { 826 | startFlash = false; 827 | flashStartTime = millis(); 828 | } 829 | 830 | if ((millis() - flashStartTime) <= flashLength) { 831 | if ((millis() - flashStartTime) % 1000 <= 500) { 832 | setColor(flashRed, flashGreen, flashBlue); 833 | } 834 | else { 835 | setColor(0, 0, 0); 836 | // If you'd prefer the flashing to happen "on top of" 837 | // the current color, uncomment the next line. 838 | // setColor(realRed, realGreen, realBlue); 839 | } 840 | } 841 | else { 842 | flash = false; 843 | effectString = oldeffectString; 844 | if (onbeforeflash) { //keeps light off after flash if light was originally off 845 | setColor(realRed, realGreen, realBlue); 846 | } 847 | else { 848 | stateOn = false; 849 | setColor(0, 0, 0); 850 | sendState(); 851 | } 852 | } 853 | } 854 | 855 | if (startFade && effectString == "solid") { 856 | // If we don't want to fade, skip it. 857 | if (transitionTime == 0) { 858 | setColor(realRed, realGreen, realBlue); 859 | 860 | redVal = realRed; 861 | grnVal = realGreen; 862 | bluVal = realBlue; 863 | 864 | startFade = false; 865 | } 866 | else { 867 | loopCount = 0; 868 | stepR = calculateStep(redVal, realRed); 869 | stepG = calculateStep(grnVal, realGreen); 870 | stepB = calculateStep(bluVal, realBlue); 871 | 872 | inFade = true; 873 | } 874 | } 875 | 876 | if (inFade) { 877 | startFade = false; 878 | unsigned long now = millis(); 879 | if (now - lastLoop > transitionTime) { 880 | if (loopCount <= 1020) { 881 | lastLoop = now; 882 | 883 | redVal = calculateVal(stepR, redVal, loopCount); 884 | grnVal = calculateVal(stepG, grnVal, loopCount); 885 | bluVal = calculateVal(stepB, bluVal, loopCount); 886 | 887 | if (effectString == "solid") { 888 | setColor(redVal, grnVal, bluVal); // Write current values to LED pins 889 | } 890 | loopCount++; 891 | } 892 | else { 893 | inFade = false; 894 | } 895 | } 896 | } 897 | } 898 | 899 | 900 | /**************************** START TRANSITION FADER *****************************************/ 901 | // From https://www.arduino.cc/en/Tutorial/ColorCrossfader 902 | /* BELOW THIS LINE IS THE MATH -- YOU SHOULDN'T NEED TO CHANGE THIS FOR THE BASICS 903 | The program works like this: 904 | Imagine a crossfade that moves the red LED from 0-10, 905 | the green from 0-5, and the blue from 10 to 7, in 906 | ten steps. 907 | We'd want to count the 10 steps and increase or 908 | decrease color values in evenly stepped increments. 909 | Imagine a + indicates raising a value by 1, and a - 910 | equals lowering it. Our 10 step fade would look like: 911 | 1 2 3 4 5 6 7 8 9 10 912 | R + + + + + + + + + + 913 | G + + + + + 914 | B - - - 915 | The red rises from 0 to 10 in ten steps, the green from 916 | 0-5 in 5 steps, and the blue falls from 10 to 7 in three steps. 917 | In the real program, the color percentages are converted to 918 | 0-255 values, and there are 1020 steps (255*4). 919 | To figure out how big a step there should be between one up- or 920 | down-tick of one of the LED values, we call calculateStep(), 921 | which calculates the absolute gap between the start and end values, 922 | and then divides that gap by 1020 to determine the size of the step 923 | between adjustments in the value. 924 | */ 925 | int calculateStep(int prevValue, int endValue) { 926 | int step = endValue - prevValue; // What's the overall gap? 927 | if (step) { // If its non-zero, 928 | step = 1020 / step; // divide by 1020 929 | } 930 | 931 | return step; 932 | } 933 | /* The next function is calculateVal. When the loop value, i, 934 | reaches the step size appropriate for one of the 935 | colors, it increases or decreases the value of that color by 1. 936 | (R, G, and B are each calculated separately.) 937 | */ 938 | int calculateVal(int step, int val, int i) { 939 | if ((step) && i % step == 0) { // If step is non-zero and its time to change a value, 940 | if (step > 0) { // increment the value if step is positive... 941 | val += 1; 942 | } 943 | else if (step < 0) { // ...or decrement it if step is negative 944 | val -= 1; 945 | } 946 | } 947 | 948 | // Defensive driving: make sure val stays in the range 0-255 949 | if (val > 255) { 950 | val = 255; 951 | } 952 | else if (val < 0) { 953 | val = 0; 954 | } 955 | 956 | return val; 957 | } 958 | 959 | 960 | 961 | /**************************** START STRIPLED PALETTE *****************************************/ 962 | void setupStripedPalette( CRGB A, CRGB AB, CRGB B, CRGB BA) { 963 | currentPalettestriped = CRGBPalette16( 964 | A, A, A, A, A, A, A, A, B, B, B, B, B, B, B, B 965 | // A, A, A, A, A, A, A, A, B, B, B, B, B, B, B, B 966 | ); 967 | } 968 | 969 | 970 | 971 | /********************************** START FADE************************************************/ 972 | void fadeall() { 973 | for (int i = 0; i < NUM_LEDS; i++) { 974 | leds[i].nscale8(250); //for CYCLon 975 | } 976 | } 977 | 978 | 979 | 980 | /********************************** START FIRE **********************************************/ 981 | void Fire2012WithPalette() 982 | { 983 | // Array of temperature readings at each simulation cell 984 | static byte heat[NUM_LEDS]; 985 | 986 | // Step 1. Cool down every cell a little 987 | for ( int i = 0; i < NUM_LEDS; i++) { 988 | heat[i] = qsub8( heat[i], random8(0, ((COOLING * 10) / NUM_LEDS) + 2)); 989 | } 990 | 991 | // Step 2. Heat from each cell drifts 'up' and diffuses a little 992 | for ( int k = NUM_LEDS - 1; k >= 2; k--) { 993 | heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3; 994 | } 995 | 996 | // Step 3. Randomly ignite new 'sparks' of heat near the bottom 997 | if ( random8() < SPARKING ) { 998 | int y = random8(7); 999 | heat[y] = qadd8( heat[y], random8(160, 255) ); 1000 | } 1001 | 1002 | // Step 4. Map from heat cells to LED colors 1003 | for ( int j = 0; j < NUM_LEDS; j++) { 1004 | // Scale the heat value from 0-255 down to 0-240 1005 | // for best results with color palettes. 1006 | byte colorindex = scale8( heat[j], 240); 1007 | CRGB color = ColorFromPalette( gPal, colorindex); 1008 | int pixelnumber; 1009 | if ( gReverseDirection ) { 1010 | pixelnumber = (NUM_LEDS - 1) - j; 1011 | } else { 1012 | pixelnumber = j; 1013 | } 1014 | leds[pixelnumber] = color; 1015 | } 1016 | } 1017 | 1018 | 1019 | 1020 | /********************************** START ADD GLITTER *********************************************/ 1021 | void addGlitter( fract8 chanceOfGlitter) 1022 | { 1023 | if ( random8() < chanceOfGlitter) { 1024 | leds[ random16(NUM_LEDS) ] += CRGB::White; 1025 | } 1026 | } 1027 | 1028 | 1029 | 1030 | /********************************** START ADD GLITTER COLOR ****************************************/ 1031 | void addGlitterColor( fract8 chanceOfGlitter, int red, int green, int blue) 1032 | { 1033 | if ( random8() < chanceOfGlitter) { 1034 | leds[ random16(NUM_LEDS) ] += CRGB(red, green, blue); 1035 | } 1036 | } 1037 | 1038 | 1039 | 1040 | /********************************** START SHOW LEDS ***********************************************/ 1041 | void showleds() { 1042 | 1043 | delay(1); 1044 | 1045 | if (stateOn) { 1046 | FastLED.setBrightness(brightness); //EXECUTE EFFECT COLOR 1047 | FastLED.show(); 1048 | if (transitionTime > 0 && transitionTime < 130) { //Sets animation speed based on receieved value 1049 | FastLED.delay(1000 / transitionTime); 1050 | //delay(10*transitionTime); 1051 | } 1052 | } 1053 | else if (startFade) { 1054 | setColor(0, 0, 0); 1055 | startFade = false; 1056 | } 1057 | } 1058 | void temp2rgb(unsigned int kelvin) { 1059 | int tmp_internal = kelvin / 100.0; 1060 | 1061 | // red 1062 | if (tmp_internal <= 66) { 1063 | red = 255; 1064 | } else { 1065 | float tmp_red = 329.698727446 * pow(tmp_internal - 60, -0.1332047592); 1066 | if (tmp_red < 0) { 1067 | red = 0; 1068 | } else if (tmp_red > 255) { 1069 | red = 255; 1070 | } else { 1071 | red = tmp_red; 1072 | } 1073 | } 1074 | 1075 | // green 1076 | if (tmp_internal <=66){ 1077 | float tmp_green = 99.4708025861 * log(tmp_internal) - 161.1195681661; 1078 | if (tmp_green < 0) { 1079 | green = 0; 1080 | } else if (tmp_green > 255) { 1081 | green = 255; 1082 | } else { 1083 | green = tmp_green; 1084 | } 1085 | } else { 1086 | float tmp_green = 288.1221695283 * pow(tmp_internal - 60, -0.0755148492); 1087 | if (tmp_green < 0) { 1088 | green = 0; 1089 | } else if (tmp_green > 255) { 1090 | green = 255; 1091 | } else { 1092 | green = tmp_green; 1093 | } 1094 | } 1095 | 1096 | // blue 1097 | if (tmp_internal >=66) { 1098 | blue = 255; 1099 | } else if (tmp_internal <= 19) { 1100 | blue = 0; 1101 | } else { 1102 | float tmp_blue = 138.5177312231 * log(tmp_internal - 10) - 305.0447927307; 1103 | if (tmp_blue < 0) { 1104 | blue = 0; 1105 | } else if (tmp_blue > 255) { 1106 | blue = 255; 1107 | } else { 1108 | blue = tmp_blue; 1109 | } 1110 | } 1111 | } 1112 | -------------------------------------------------------------------------------- /Example Home Assistant Configuration.yaml: -------------------------------------------------------------------------------- 1 | mqtt: 2 | broker: your.mqtt.ip.address 3 | port: 1883 4 | client_id: home-assistant-1 5 | username: YOURUSERNAME 6 | password: YOURPASSWORD 7 | 8 | light: 9 | - platform: mqtt_json 10 | name: "Porch Strip" 11 | state_topic: "bruh/porch" 12 | command_topic: "bruh/porch/set" 13 | effect: true 14 | effect_list: 15 | - bpm 16 | - candy cane 17 | - confetti 18 | - cyclon rainbow 19 | - dots 20 | - fire 21 | - glitter 22 | - juggle 23 | - lightning 24 | - noise 25 | - police all 26 | - police one 27 | - rainbow 28 | - rainbow with glitter 29 | - ripple 30 | - sinelon 31 | - solid 32 | - twinkle 33 | brightness: true 34 | flash: true 35 | rgb: true 36 | optimistic: false 37 | qos: 0 38 | 39 | input_slider: 40 | porch_animation_speed: 41 | name: Porch Animation Speed 42 | initial: 150 43 | min: 1 44 | max: 150 45 | step: 10 46 | 47 | automation: 48 | - alias: "Porch Animation Speed" 49 | initial_state: True 50 | hide_entity: False 51 | trigger: 52 | - platform: state 53 | entity_id: input_slider.porch_animation_speed 54 | action: 55 | - service: mqtt.publish 56 | data_template: 57 | topic: "bruh/porch/set" 58 | payload: '{"transition":{{ trigger.to_state.state | int }}}' 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP MQTT JSON Digital LEDs 2 | 3 | This project shows a super easy way to get started using Digital LED strips with [Home Assistant](https://home-assistant.io/), a sick, open-source Home Automation platform that can do just about anything. 4 | 5 | The code covered in this repository utilizes [Home Assistant's MQTT JSON Light Component](https://home-assistant.io/components/light.mqtt_json/) and an ESP8266 microcontroller. 6 | 7 | #### Supported Features Include 8 | - RGB Color Selection 9 | - Brightness 10 | - Flash 11 | - Fade 12 | - Transitions 13 | - Effects with Animation Speed 14 | - Over-the-Air (OTA) Upload from the ArduinoIDE! 15 | 16 | Some of the effects incorporate the currrently selected color (sinelon, confetti, juggle, etc) while other effects use pre-defined colors. You can also select custom transition speeds between colors. The transition variable in Home Assistant (HA) also functions to control the animation speed of the currently running animation. The input_slider and automation in the HA configuration example allow you to easily set a transition speed from HA's user interface without needing to use the Services tool. 17 | 18 | The default speed for the effects is hard coded and is set when the light is first turned on. When changing between effects, the previously used transition speed will take over. If the effects don't look great, play around with the slider to adjust the transition speed (AKA the effect's animation speed). 19 | 20 | #### OTA Uploading 21 | This code also supports remote uploading to the ESP8266 using Arduino's OTA library. To utilize this, you'll need to first upload the sketch using the traditional USB method. However, if you need to update your code after that, your WIFI-connected ESP chip should show up as an option under Tools -> Port -> Porch at your.ip.address.xxx. More information on OTA uploading can be found [here](http://esp8266.github.io/Arduino/versions/2.0.0/doc/ota_updates/ota_updates.html). Note: You cannot access the serial monitor over WIFI at this point. 22 | 23 | 24 | #### Demo Video 25 | [![Demo Video](http://i.imgur.com/cpW2JAX.png)](https://www.youtube.com/watch?v=DQZ4x6Z3678 "Demo - RGB Digital LED Strip controlled using ESP, MQTT, and Home Assistant") 26 | 27 | #### Tutorial Video 28 | [![Tutorial Video](http://i.imgur.com/9UMl8Xo.jpg)](https://www.youtube.com/watch?v=9KI36GTgwuQ "The BEST Digital LED Strip Light Tutorial - DIY, WIFI-Controllable via ESP, MQTT, and Home Assistant") 29 | 30 | #### Parts List 31 | - [Digital RGB Leds](http://geni.us/8mBml) 32 | - [NodeMCU](http://geni.us/4pVoT) 33 | - [Aluminum Mounting Channel/Diffuser](http://geni.us/JBDhv7) 34 | - [12v to 5v Step Down](http://geni.us/PghhV9) 35 | - [12V 15amp Power Supply](http://geni.us/8rKC) 36 | - [Strip Connector](http://geni.us/OL7tHv) 37 | - [Logic Level Shifter](http://geni.us/4hJAyy) 38 | - [20 Gauge Wire](http://geni.us/2MBYAXF) 39 | - [Cable Chase](http://geni.us/lFqD) 40 | - [Project Box](http://geni.us/kZRgaj) 41 | - [Header Wires](http://geni.us/GniKAX) 42 | - [Power Jacks](http://geni.us/7Ywdut) 43 | 44 | 45 | #### Wiring Diagram 46 | ![alt text](https://github.com/bruhautomation/ESP-MQTT-Digital-LEDs/blob/master/ESP%20MQTT%20Digital%20LEDs%20Wiring%20Diagram.png?raw=true "Wiring Diagram") 47 | 48 | 49 | #### Home Assistant Service Examples 50 | Besides using the card in Home Assistant's user interface, you can also use the Services tool to control the light using the light.turn_on and light.turn_off services. This will let you play with the parameters you can call later in automations or scripts. 51 | 52 | Fade the Light On Over 5 Seconds - light.turn_on 53 | ``` 54 | {"entity_id":"light.porch_strip", 55 | "brightness":150, 56 | "color_name":"blue", 57 | "transition":"5" 58 | } 59 | ``` 60 | 61 | Flash The Light - light.turn_on 62 | ``` 63 | {"entity_id":"light.porch_strip", 64 | "color_name":"green", 65 | "brightness":255, 66 | "flash":"short" 67 | } 68 | ``` 69 | 70 | Call Rainbow Effect with Slow Animation Speed - light.turn_on 71 | ``` 72 | {"entity_id":"light.porch_strip", 73 | "transition":"50", 74 | "brightness":255, 75 | "effect":"rainbow" 76 | } 77 | ``` 78 | 79 | Fade the Light Off Over 5 Seconds - light.turn_off 80 | ``` 81 | {"entity_id":"light.porch_strip", 82 | "transition":"50" 83 | } 84 | ``` 85 | --------------------------------------------------------------------------------