diff --git a/code/espurna/light_ir.ino b/code/espurna/light_ir.ino new file mode 100644 index 00000000..2d013756 --- /dev/null +++ b/code/espurna/light_ir.ino @@ -0,0 +1,1163 @@ +/* + +LIGHT (EXPERIMENTAL) IR + +Copyright (C) 2016-2017 by Xose Pérez +Copyright (C) 2017 by François Déchery + +------------------------------------------------------------------------------------------ +Features : + - ONLY RGB strips are supported (No RGBW or RGBWW) + - IR remote supported (but not mandatory) + - HSV (intuitive) WEB controls + - MQTT & API "color_rgb" + "color_hsv" + "brightness" parameters + - Uses the (amazing) Fastled library for fast and natural Color & Brightness + - Several Animation Modes and Speed (controlled from Remote + Web + MQTT + API) +------------------------------------------------------------------------------------------ +Not currently Implemented : + - Saving/Restoring Settings + - HomeAssistant +------------------------------------------------------------------------------------------ + +*/ + + +#ifdef LIGHT_PROVIDER_EXPERIMENTAL_RGB_ONLY_HSV_IR + +#include +#include +#include + +// #### Defined ########################################################################## +#define ANIM_SPEED_STEP 20 +#define ANIM1_SPEED 350 // flash ON Variable +#define ANIM1_PAUSE 200 // flash OFF fixed +#define ANIM2_SPEED 550 // strobe OFF variable +#define ANIM2_PAUSE 150 // storbe ON fixed +#define ANIM3_SPEED 100 // fade speed +#define ANIM4_SPEED 700 // smooth speed +#define ANIM5_SPEED 200 // party speed + +#define BUTTONS_COUNT 24 +#define LED_DURATION 70 // Status led ON duration + +// #### Variables ######################################################################## +unsigned long r_but_codes[]={ // IR remote buttons codes + IR_BUTTON_0 , // Brightness + + IR_BUTTON_1 , // Brightness - + IR_BUTTON_2 , // OFF + IR_BUTTON_3 , // ON + IR_BUTTON_4 , // Red + IR_BUTTON_5 , // Green + IR_BUTTON_6 , // Blue + IR_BUTTON_7 , // White + IR_BUTTON_8 , // R1 + IR_BUTTON_9 , // G1 + IR_BUTTON_10 , // B1 + IR_BUTTON_11 , // Flash + IR_BUTTON_12 , // R2 + IR_BUTTON_13 , // G2 + IR_BUTTON_14 , // B2 + IR_BUTTON_15 , // Strobe + IR_BUTTON_16 , // R3 + IR_BUTTON_17 , // G3 + IR_BUTTON_18 , // B3 + IR_BUTTON_19 , // Fade + IR_BUTTON_20 , // R4 + IR_BUTTON_21 , // G4 + IR_BUTTON_22 , // B4 + IR_BUTTON_23 // Smooth +}; + +unsigned long r_but_colors[]={ // IR remote buttons colors + 0, // Brightness + + 0, // Brightness - + 0, // OFF + 0, // ON + 0xFF0000, // Red + 0x00FF00, // Green + 0x0000FF, // Blue + 0xFFFFFF, // White + 0xD13A01, // R1 + 0x00E644, // G1 + 0x0040A7, // B1 + 0, // Flash + 0xE96F2A, // R2 + 0x00BEBF, // G2 + 0x56406F, // B2 + 0, // Strobe + 0xEE9819, // R3 + 0x00799A, // G3 + 0x944E80, // B3 + 0, // Fade + 0xFFFF00, // R4 + 0x0060A1, // G4 + 0xEF45AD, // B4 + 0 // Smooth +}; + +// variables declarations ############################################################### +CHSV _cur_color = CHSV(0,255,255); +CHSV _cur_anim_color = CHSV(0,0,0); +byte _cur_status = 0 ; +byte _cur_anim_mode = 0 ; +byte _cur_anim_step = 0; +boolean _cur_anim_dir = true; +unsigned long _cur_anim_speed = 1000; +unsigned long _anim_last_update = millis(); +unsigned long _last_ir_button = 0; +unsigned long _last_status_led_time = 0; + +IRrecv _ir_recv(LIGHT_IR_PIN); //IRrecv _ir_recv(IR_PIN, IR_LED_PIN); dont work. Why ? +decode_results _ir_results; + + +// ####################################################################################### +// #### PRIVATE ########################################################################## +// ####################################################################################### + +// --------------------------------------------------------------------------------------- +void _updateStatusLed(){ + if(millis() > _last_status_led_time + LED_DURATION ){ + digitalWrite(LED1_PIN, LOW); + } +} + +// --------------------------------------------------------------------------------------- +void _flashStatusLed(){ + digitalWrite(LED1_PIN, HIGH); + _last_status_led_time=millis(); +} + +// --------------------------------------------------------------------------------------- +void _loopProcessIR() { + if (_ir_recv.decode(&_ir_results)) { + //dumpIR(&_ir_results); + //DEBUG_MSG_P(PSTR(".\n")); + unsigned long code = _ir_results.value; + DEBUG_MSG_P(PSTR("[IR] received : 0x%X "), code ); + if( code == 0xFFFFFFFF){ + code = _last_ir_button; + DEBUG_MSG_P(PSTR("(Repeat : %X) "), code ); + } + DEBUG_MSG_P(PSTR("=> ") ); + _processIrButtons(code); + _ir_recv.resume(); // Receive the next value + } +} + +// --------------------------------------------------------------------------------------- +void _processIrButtons(unsigned long code) { + //DEBUG_MSG_P(PSTR("IR code : %X\n"), code ); + boolean done=false; + for (int i = 0; i < BUTTONS_COUNT ; i = i + 1) { + if( code == r_but_codes[i] ){ + //DEBUG_MSG_P(PSTR(" : %X -> "), r_but_colors[i] ); + + _last_ir_button = 0; //no repat else if specified + + if(i == 0){ + _buttonBrightness(true); + _last_ir_button = code; + delay(150); //debounce + } + else if(i == 1){ + _buttonBrightness(false); + _last_ir_button = code; + delay(150); //debounce + } + else if(i == 2){ + _buttonPower(false); + } + else if(i == 3){ + _buttonPower(true); + } + else if(i == 11){ + _buttonAnimMode(1); + } + else if(i == 15){ + _buttonAnimMode(2); + } + else if(i == 19){ + _buttonAnimMode(3); + } + else if(i == 23){ + _buttonAnimMode(4); + } + else{ + _buttonColorRVB(r_but_colors[i],0); + } + done=true; + lightUpdate(true, true); + } + } + if(!done){ + _last_ir_button = 0; + //DEBUG_PRINTHEX(code); + DEBUG_MSG_P(PSTR("ignored!\n")); + } +} + +// --------------------------------------------------------------------------------------- +void _buttonPower(boolean on){ + _flashStatusLed(); + DEBUG_MSG_P(PSTR("BUT Power: ")); + _cur_anim_mode=0; + if(on){ + _cur_status=1; + DEBUG_MSG_P(PSTR("ON : ")); + _setLedsHSV(_cur_color); + } + else{ + _cur_status=0; + DEBUG_MSG_P(PSTR("OFF : ")); + _setLedsHSV(CHSV {0,0,0}); + } +} + +// --------------------------------------------------------------------------------------- +void _buttonAnimMode(byte val){ + _flashStatusLed(); + DEBUG_MSG_P(PSTR("BUT Anim Mode: %d\n"),val); + _processAnimation(val, true, true); +} + +// --------------------------------------------------------------------------------------- +void _setAnimMode(byte val){ + DEBUG_MSG_P(PSTR("[LIGHT] Set AnimMode: %d\n"),val); + _processAnimation(val, true, false); +} + +// --------------------------------------------------------------------------------------- +void _buttonBrightness(boolean up){ + DEBUG_MSG_P(PSTR("BUT Brightness: ")); + if(up){ + if(_cur_anim_mode==0){ + DEBUG_MSG_P(PSTR("UP : ")); + _buttonColorHSV(_cur_color, 1 ); + } + else{ + DEBUG_MSG_P(PSTR("FASTER\n")); + _buttonChangeSpeed( - ANIM_SPEED_STEP ); + } + } + else{ + if(_cur_anim_mode==0){ + DEBUG_MSG_P(PSTR("DOWN : ")); + _buttonColorHSV(_cur_color, -1 ); + } + else{ + DEBUG_MSG_P(PSTR("SLOWER\n")); + _buttonChangeSpeed( ANIM_SPEED_STEP ); + } + } +} + +// --------------------------------------------------------------------------------------- +void _buttonColorHSV(CHSV color, int offset){ + _flashStatusLed(); + DEBUG_MSG_P(PSTR("[LIGHT] Set Color to : ")); + //DEBUG_MSG_P(PSTR("(from HSV=%d,%d,%d) "), color.h, color.s, color.v); + color=_dimHSV(color,offset); + //DEBUG_MSG_P(PSTR("(to HSV=%d,%d,%d) "), color.h, color.s, color.v); + _setLedsHSV(color); + if(color.v ==0 ){ + _cur_status=0; + } + else{ + _cur_status=1; + } + _cur_color=color; + _romSaveCurrentColor(); +} + +// --------------------------------------------------------------------------------------- +void _buttonColorRVB(CRGB color, int offset){ + _buttonColorHSV(_rgbToHsv(color), offset); +} + +// --------------------------------------------------------------------------------------- +void _buttonChangeSpeed(int offset){ + _flashStatusLed(); + if(offset !=0){ + _cur_anim_speed=_cur_anim_speed + offset ; + } +} + +// --------------------------------------------------------------------------------------- +CHSV _dimHSV(CHSV color, int offset){ + offset=offset*10; + int bright=color.v + offset; + if(offset ==0){ + return color; + } + else if(bright < 1){ + bright=1; // no off + } + else if(bright > 255){ + bright=255; + } + color.v=bright; + return color; +} + +// --------------------------------------------------------------------------------------- +void _setBrightnessMapped(byte val){ + DEBUG_MSG_P(PSTR("[LIGHT] Set from 0-100 Brightness : %u => "), val); + val =map(val,0,100,0,255); + _setBrightness(val); +} + +// --------------------------------------------------------------------------------------- +void _setBrightness(byte val){ + DEBUG_MSG_P(PSTR("[LIGHT] Set Brightness to : %u\n"), val); + _cur_color.v = val; + _cur_anim_color.v = val; + if(val==0){ + _cur_status=0; + } + _buttonColorHSV(_cur_color,0); +} + +// --------------------------------------------------------------------------------------- +void _setAnimSpeed(unsigned long speed){ + if(speed !=0){ + _cur_anim_speed=speed ; + } +} + +// --------------------------------------------------------------------------------------- +void _setLedsRGB(CRGB rgb){ + analogWrite(LIGHT_CH1_PIN, rgb.r); + analogWrite(LIGHT_CH2_PIN, rgb.g); + analogWrite(LIGHT_CH3_PIN, rgb.b); + if(_cur_anim_mode == 0){ + DEBUG_MSG_P(PSTR("RGB=%3u,%3u,%3u\n"), rgb.r, rgb.g, rgb.b); + } +} + +// --------------------------------------------------------------------------------------- +void _setLedsHSV(CHSV hsv){ + if(_cur_anim_mode == 0){ + DEBUG_MSG_P(PSTR("HSV=%3u,%3u,%3u - "), hsv.h, hsv.s, hsv.v); + } + _setLedsRGB( CHSV(hsv) ); +} + +// --------------------------------------------------------------------------------------- +void _confirmFlash(){ + _setLedsRGB(CRGB::Black); + delay(70); + + _setLedsRGB(CRGB::White); + delay(70); + + _setLedsRGB(CRGB::Black); + delay(70); + + _setLedsRGB(CRGB::White); + delay(70); + + _setLedsRGB(CRGB::Black); + delay(70); +} + +// --------------------------------------------------------------------------------------- +void _processAnimation(byte mode, boolean init, boolean is_button){ + if(init){ + if(_cur_anim_mode == mode && is_button){ + _confirmFlash(); + DEBUG_MSG_P(PSTR("[ANIM_%d] Stopped !!!\n"), mode); + _cur_anim_mode=0; + _cur_status=0; + return; + } + else if(mode == 0){ + DEBUG_MSG_P(PSTR("[ANIM_%d] Stopped !!!\n"), mode); + _cur_anim_mode=0; + _cur_status=0; + return; + } + else{ + if(is_button){ + _confirmFlash(); + } + DEBUG_MSG_P(PSTR("[ANIM_%d] Started !!!\n"), mode); + } + } + + if(mode==1){ + _anim1(init); + } + else if(mode==2){ + _anim2(init); + } + else if(mode==3){ + _anim3(init); + } + else if(mode==4){ + _anim4(init); + } + else if(mode==5){ + _anim5(init); + } + else{ + //invalid mode + } +} + +// --------------------------------------------------------------------------------------- +// anim1 : flash +void _anim1(boolean init){ + if(init){ + _cur_anim_mode = 1; + _cur_anim_speed = ANIM1_SPEED; + _cur_status = 1; + _cur_anim_step = 1; + } + unsigned long now= millis(); + if(_cur_anim_step==1 && now > (_anim_last_update + _cur_anim_speed) ){ + //DEBUG_MSG_P(PSTR("[ANIM_%d] Update : "), _cur_anim_mode); + _setLedsHSV(_cur_color); + _cur_anim_step=0; + _anim_last_update = now; + } + else if(_cur_anim_step==0 && now > (_anim_last_update + ANIM1_PAUSE) ){ + //DEBUG_MSG_P(PSTR("[ANIM_%d] Update : "), _cur_anim_mode); + _setLedsHSV(CHSV {0,0,0}); + _cur_anim_step=1; + _anim_last_update = now; + } +} + +// --------------------------------------------------------------------------------------- +// anim2 : strobe +void _anim2(boolean init){ + if(init){ + _cur_anim_mode = 2; + _cur_anim_speed = ANIM2_SPEED; + _cur_status = 1; + _cur_anim_step = 1; + } + unsigned long now= millis(); + if(_cur_anim_step==1 && now > (_anim_last_update + ANIM2_PAUSE) ){ + //DEBUG_MSG_P(PSTR("[ANIM_%d] Update : "), _cur_anim_mode); + _setLedsHSV(_cur_color); + _cur_anim_step=0; + _anim_last_update = now; + } + else if(_cur_anim_step==0 && now > (_anim_last_update + _cur_anim_speed) ){ + //DEBUG_MSG_P(PSTR("[ANIM_%d] Update : "), _cur_anim_mode); + _setLedsHSV(CHSV {0,0,0}); + _cur_anim_step=1; + _anim_last_update = now; + } +} + +// --------------------------------------------------------------------------------------- +// anim3 : fade +void _anim3(boolean init){ + if(init){ + _cur_anim_mode = 3; + _cur_anim_speed = ANIM3_SPEED; + _cur_status = 1; + _cur_anim_step = _cur_color.v; + } + + unsigned long now= millis(); + if( now > (_anim_last_update + _cur_anim_speed) ){ + //DEBUG_MSG_P(PSTR("[ANIM_%d] Update : "), _cur_anim_mode); + _setLedsHSV( CHSV(_cur_color.h, _cur_color.s, dim8_lin(_cur_anim_step)) ); + if(_cur_anim_dir){ + if(_cur_anim_step == 255){ + _cur_anim_dir=false; + } + else{ + _cur_anim_step++; + } + } + else{ + if(_cur_anim_step == 1){ + _cur_anim_dir=true; + } + else{ + _cur_anim_step--; + } + } + _anim_last_update = now; + } + +} + +// --------------------------------------------------------------------------------------- +// anim4 : smooth +void _anim4(boolean init){ + if(init){ + _cur_anim_mode = 4; + _cur_anim_speed = ANIM4_SPEED; + _cur_status = 1; + _cur_anim_step = 0; + _cur_anim_color = _cur_color; + _cur_anim_color.v = 255; + } + + unsigned long now= millis(); + if( now > (_anim_last_update + _cur_anim_speed) ){ + //DEBUG_MSG_P(PSTR("[ANIM_%d] Update : "), _cur_anim_mode); + _cur_anim_color.h=_cur_anim_step; + _cur_anim_color.s=255; + _setLedsHSV( CHSV(_cur_anim_step,255,255) ); + _cur_anim_step++; + _anim_last_update = now; + if(_cur_anim_step > 255){ + _cur_anim_step=0; + } + } +} + +// --------------------------------------------------------------------------------------- +// anim5 : party +void _anim5(boolean init){ + if(init){ + _cur_anim_mode = 5; + _cur_anim_speed = ANIM5_SPEED; + _cur_status = 1; + _cur_anim_step = 0; + _cur_anim_color = _cur_color; + _cur_anim_color.s= 255; + _cur_anim_color.v= 255; + } + unsigned long now= millis(); + if(_cur_anim_step == 1 && now > (_anim_last_update + _cur_anim_speed) ){ + DEBUG_MSG_P(PSTR("[ANIM_%d] Update : "), _cur_anim_mode); + _cur_anim_color.h = random(0,255); + _setLedsHSV(_cur_anim_color); + _cur_anim_step = 0; + _anim_last_update = now; + } + else if(_cur_anim_step==0 && now > (_anim_last_update + _cur_anim_speed + 15) ){ + //DEBUG_MSG_P(PSTR("[ANIM_%d] Update : "), _cur_anim_mode); + _setLedsHSV(CHSV {0,0,0}); + _cur_anim_step = 1; + _anim_last_update = now; + } +} + +// --------------------------------------------------------------------------------------- +void _loopUpdateAnimation(){ + _processAnimation(_cur_anim_mode, false, false); +} + +// --------------------------------------------------------------------------------------- +CHSV _rgbToHsv(CRGB rgb){ +/* + if(rgb.r <=1 && rgb.g <=1 && rgb.b <=1){ + DEBUG_MSG_P(PSTR(" {Rounding RGB to black} ")); + return CHSV {0,0,0}; + } +*/ + return rgb2hsv_approximate(rgb); +} + +// --------------------------------------------------------------------------------------- +void _romSaveCurrentColor(){ + //save it to 1,2,3 positions + //but dont stress eeprom if not needed +/* + CHSV rom =_romLoadColor(); + if(_cur_color.h != rom.h){gw.saveState(1,_cur_color.h);} + if(_cur_color.s != rom.s){gw.saveState(2,_cur_color.s);} + if(_cur_color.v != rom.v){gw.saveState(3,_cur_color.v);} +*/ +} + +// --------------------------------------------------------------------------------------- +CHSV _romLoadColor(){ + //load from 1,2,3 positions + CHSV color; +/* + color.h=gw.loadState(1); + color.s=gw.loadState(2); + color.v=gw.loadState(3); +*/ + return color; +} + +// ---------------------------------------------- +CRGB _longToRgb(unsigned long rgb){ + CRGB out; + out.r = rgb >> 16; + out.g = rgb >> 8 & 0xFF; + out.b = rgb & 0xFF; + return out; +} + +// --------------------------------------------------------------------------------------- +CHSV _longToHsv(unsigned long hsv){ + CHSV out; + out.h = hsv >> 16; + out.s = hsv >> 8 & 0xFF; + out.v = hsv & 0xFF; + return out; +} + +// --------------------------------------------------------------------------------------- +unsigned long _rgbToLong(CRGB in){ + return (((long)in.r & 0xFF) << 16) + (((long)in.g & 0xFF) << 8) + ((long)in.b & 0xFF); +} + +// --------------------------------------------------------------------------------------- +unsigned long _hsvToLong(CHSV in){ + return (((long)in.h & 0xFF) << 16) + (((long)in.s & 0xFF) << 8) + ((long)in.v & 0xFF); +} + +// --------------------------------------------------------------------------------------- +CRGB _charToRgb(const char * rgb) { + + char * p = (char *) rgb; + + // if color begins with a # then assume HEX RGB + if (p[0] == '#') { + ++p; + } + return _longToRgb( strtoul(p, NULL, 16) ); +} + +// --------------------------------------------------------------------------------------- +CHSV _charToHsv(const char * rgb) { + + char * p = (char *) rgb; + + // if color begins with a # then assume HEX RGB + if (p[0] == '#') { + ++p; + } + return _longToHsv( strtoul(p, NULL, 16) ); +} + + +// --------------------------------------------------------------------------------------- +boolean _charColorIsValid(const char * rgb){ + char * p = (char *) rgb; + if (strlen(p) == 6 || strlen(p) == 7 ){ + return true; + } + return false; +} + +// --------------------------------------------------------------------------------------- +void _CurrentColorToRGB(char * buffer) { + snprintf_P(buffer, 8, PSTR("%06X"), _rgbToLong( CHSV(_cur_color))); +} + +// --------------------------------------------------------------------------------------- +void _CurrentColorToHSV(char * buffer) { + snprintf_P(buffer, 8, PSTR("%06X"), _hsvToLong( _cur_color)); +} + +// --------------------------------------------------------------------------------------- +void _lightColorRestore() { +/* + for (unsigned int i=0; i < _channels.size(); i++) { + _channels[i].value = getSetting("ch", i, 0).toInt(); + } + _brightness = getSetting("brightness", LIGHT_MAX_BRIGHTNESS).toInt(); + lightUpdate(false, false); +*/ +} + + +// --------------------------------------------------------------------------------------- +void _lightAPISetup() { + + #if WEB_SUPPORT + + // API entry points (protected with apikey) + if (lightHasColor()) { + + apiRegister(MQTT_TOPIC_COLOR_RGB, MQTT_TOPIC_COLOR_RGB, + [](char * buffer, size_t len) { + _CurrentColorToRGB(buffer); + }, + [](const char * payload) { + _SetLightColorRGB(payload); + lightUpdate(true, true); + } + ); + + apiRegister(MQTT_TOPIC_COLOR_HSV, MQTT_TOPIC_COLOR_HSV, + [](char * buffer, size_t len) { + _CurrentColorToHSV(buffer); + }, + [](const char * payload) { + _SetLightColorHSV(payload); + lightUpdate(true, true); + } + ); + + apiRegister(MQTT_TOPIC_BRIGHTNESS, MQTT_TOPIC_BRIGHTNESS, + [](char * buffer, size_t len) { + snprintf_P(buffer, len, PSTR("%d"), _cur_color.v); + }, + [](const char * payload) { + _setBrightness(atoi(payload)); + lightUpdate(true, true); + } + ); + + apiRegister(MQTT_TOPIC_ANIM_MODE, MQTT_TOPIC_ANIM_MODE, + [](char * buffer, size_t len) { + snprintf_P(buffer, len, PSTR("%d"), _cur_anim_mode); + }, + [](const char * payload) { + _setAnimMode(atoi(payload)); + lightUpdate(true, true); + } + ); + + apiRegister(MQTT_TOPIC_ANIM_SPEED, MQTT_TOPIC_ANIM_SPEED, + [](char * buffer, size_t len) { + snprintf_P(buffer, len, PSTR("%d"), _cur_anim_speed); + }, + [](const char * payload) { + _setAnimSpeed(atoi(payload)); + lightUpdate(true, true); + } + ); + + + } +/* + for (unsigned int id=0; id "), value); + current_rvb.r=value; + _buttonColorRVB(current_rvb,0); + } + else if(id == 1 ){ + DEBUG_MSG_P(PSTR("GREEN to : %d => "), value); + current_rvb.g=value; + _buttonColorRVB(current_rvb,0); + } + else if(id == 2 ){ + DEBUG_MSG_P(PSTR("BLUE to : %d => "), value); + current_rvb.b=value; + _buttonColorRVB(current_rvb,0); + } + else{ + DEBUG_MSG_P(PSTR(" [ERROR] SET lightChannel %s To %d \n"), id, value); + } +} + +// --------------------------------------------------------------------------------------- +// Get Brightness +unsigned int lightBrightness() { + return _cur_color.v; +} + +// --------------------------------------------------------------------------------------- +// Set Brightness +void lightBrightness(unsigned int b) { + b=constrain(b, 0, 255); + DEBUG_MSG_P(PSTR("[WEB|API] Set Brightness to : %d\n"), b); + _cur_color.v=b; + _setLedsHSV(_cur_color); + + //set status + if(b > 0){ + _cur_status=1; + } + else{ + _cur_status=0; + } +} + +// --------------------------------------------------------------------------------------- +// Get Color +String lightColor() { + char rgb[8]; + snprintf_P(rgb, 8, PSTR("#%06X"), _rgbToLong( CHSV(_cur_color))); + return String(rgb); + //return String(""); +} + +String lightColorH(){ + return String(_cur_color.h); +} +String lightColorS(){ + return String(_cur_color.s); +} +String lightColorV(){ + return String(_cur_color.v); +} + + +// --------------------------------------------------------------------------------------- +// Set Color +void lightColor(const char * color) { + //used only from settings + _SetLightColorRGB(color); +} + +void _SetLightColorRGB(const char * color) { +//used only from settings + DEBUG_MSG_P(PSTR("[WEB|API] Set (#RGB) Color to : ")); + if( _charColorIsValid(color) ){ + DEBUG_MSG_P(PSTR("%s \n"), color); + _buttonColorRVB(_charToRgb(color), 0); + } + else{ + DEBUG_MSG_P(PSTR(" Canceled ('%s' is invalid) !\n"), color); + } +} + +void _SetLightColorHSV(const char * color) { + DEBUG_MSG_P(PSTR("[WEB|API] Set (#HSV) Color to : ")); + if( _charColorIsValid(color) ){ + DEBUG_MSG_P(PSTR("%s \n"), color); + _buttonColorHSV(_charToHsv(color), 0); + } + else{ + DEBUG_MSG_P(PSTR(" Canceled ('%s' is invalid) !\n"), color); + } +} + +void setLightColor (const char * h, const char * s, const char * v){ + DEBUG_MSG_P(PSTR("[WEB|API] Set Color from (%s,%s,%s) "), h, s, v); + CHSV color; + color.h=strtoul(h, NULL, 10); + color.s=strtoul(s, NULL, 10); + color.v=strtoul(v, NULL, 10); + DEBUG_MSG_P(PSTR("to (%d,%d,%d) "), color.h, color.s, color.v); + _buttonColorRVB(color, 0); +} + + +// --------------------------------------------------------------------------------------- +bool lightHasColor() { +// bool useColor = getSetting("useColor", LIGHT_USE_COLOR).toInt() == 1; +// return useColor && (_channels.size() > 2); + //if(_cur_status || _cur_anim_mode){return 1;} + return true; +} + + +// --------------------------------------------------------------------------------------- +// Get State +bool lightState() { + //DEBUG_MSG_P(PSTR("[->LIGHT] _cur_status is : %d \n"),_cur_status); + return _cur_status; +} + +// --------------------------------------------------------------------------------------- +// Set State +void lightState(bool state){ + DEBUG_MSG_P(PSTR("[WEB|API] Set Relay to : %u => "), state); + //if(state != _cur_status){ + _buttonPower(state); + //} +} + + +// --------------------------------------------------------------------------------------- +String lightAnimMode(){ + return String(_cur_anim_mode); +} +void lightAnimMode(const char * val){ + DEBUG_MSG_P(PSTR("[WEB|API] Set AnimMode to %s\n"), val); + _setAnimMode(strtoul(val, NULL, 10)); +} +// --------------------------------------------------------------------------------------- +String lightAnimSpeed(){ + return String(_cur_anim_speed); +} +void lightAnimSpeed(const char * val){ + DEBUG_MSG_P(PSTR("[WEB|API] Set AnimSpeed to %s \n"), val); + _setAnimSpeed(strtoul(val, NULL, 10)); +} + + +// --------------------------------------------------------------------------------------- +// MQTT +// --------------------------------------------------------------------------------------- + +void _lightMQTTCallback(unsigned int type, const char * topic, const char * payload) { + + + if (type == MQTT_CONNECT_EVENT) { + + if (lightHasColor()) { + mqttSubscribe(MQTT_TOPIC_BRIGHTNESS); + mqttSubscribe(MQTT_TOPIC_COLOR_RGB); + mqttSubscribe(MQTT_TOPIC_COLOR_HSV); + mqttSubscribe(MQTT_TOPIC_ANIM_MODE); + mqttSubscribe(MQTT_TOPIC_ANIM_SPEED); + } + + //char buffer[strlen(MQTT_TOPIC_CHANNEL) + 3]; + //snprintf_P(buffer, sizeof(buffer), PSTR("%s/+"), MQTT_TOPIC_CHANNEL); + //mqttSubscribe(buffer); + + } + + if (type == MQTT_MESSAGE_EVENT) { + + // Match topic + String t = mqttSubtopic((char *) topic); + + // Color RGB + if (t.equals(MQTT_TOPIC_COLOR_RGB)) { + _SetLightColorRGB(payload); + lightUpdate(true, mqttForward()); + } + + // Color HSV + if (t.equals(MQTT_TOPIC_COLOR_HSV)) { + _SetLightColorHSV(payload); + lightUpdate(true, mqttForward()); + } + + // ANIM Mode + if (t.equals(MQTT_TOPIC_ANIM_MODE)) { + _setAnimMode(atoi(payload)); + lightUpdate(true, mqttForward()); + } + + // ANIM Speed + if (t.equals(MQTT_TOPIC_ANIM_SPEED)) { + _setAnimSpeed(atoi(payload)); + lightUpdate(true, mqttForward()); + } + + // Brightness + if (t.equals(MQTT_TOPIC_BRIGHTNESS)) { + _setBrightness (constrain(atoi(payload), 0, LIGHT_MAX_BRIGHTNESS)); + lightUpdate(true, mqttForward()); + } + +/* + // Channel + if (t.startsWith(MQTT_TOPIC_CHANNEL)) { + unsigned int channelID = t.substring(strlen(MQTT_TOPIC_CHANNEL)+1).toInt(); + if (channelID >= _channels.size()) { + DEBUG_MSG_P(PSTR("[LIGHT] Wrong channelID (%d)\n"), channelID); + return; + } + lightChannel(channelID, atoi(payload)); + lightUpdate(true, mqttForward()); + } +*/ + } +} + +#endif // LIGHT_IR_PIN