├── HTTPPrinter.cpp ├── HTTPPrinter.h ├── HueBridge-example.ino ├── HueBridge.cpp ├── HueBridge.h ├── HueBridgeStructs.h └── example of massive json.txt /HTTPPrinter.cpp: -------------------------------------------------------------------------------- 1 | #include "HTTPPrinter.h" 2 | 3 | HTTPPrinter::HTTPPrinter() { 4 | _bufferCount = 0; 5 | _buffer = NULL; 6 | _size = 0; 7 | _headerSent = false; 8 | _CountMode = false; 9 | } 10 | 11 | 12 | void HTTPPrinter::Begin(WiFiClient &c) { 13 | _bufferCount = 0; 14 | _buffer = (uint8_t *)malloc(HTTPPrinterSize); 15 | _client = c; 16 | _size = 0; 17 | _headerSent = false; 18 | _CountMode = false; 19 | } 20 | 21 | void HTTPPrinter::Begin(WiFiClient &c, size_t memory) { 22 | _bufferCount = 0; 23 | _buffer = (uint8_t *)malloc(memory); 24 | _client = c; 25 | _size = 0; 26 | _headerSent = false; 27 | _CountMode = false; 28 | } 29 | 30 | void HTTPPrinter::End() { 31 | free(_buffer); 32 | } 33 | 34 | void HTTPPrinter::Setsize(size_t s) { 35 | _size = s; 36 | } 37 | 38 | size_t HTTPPrinter::SetCountMode(bool mode) { 39 | 40 | if (mode) { 41 | _CountMode = true; 42 | _size = 0; 43 | } else { 44 | _CountMode = false; 45 | _sizeTotal = _size; 46 | } 47 | 48 | return _size; 49 | 50 | } 51 | 52 | size_t HTTPPrinter::GetSize() { 53 | return _sizeTotal; 54 | } 55 | 56 | 57 | size_t HTTPPrinter::write(uint8_t data) { 58 | 59 | if (_buffer == NULL) return 0; 60 | 61 | switch(_CountMode) { 62 | 63 | case true: 64 | _size++; 65 | break; 66 | 67 | case false: 68 | 69 | _buffer[_bufferCount++] = data; // put byte into buffer 70 | 71 | if(_bufferCount == HTTPPrinterSize || _size == _bufferCount ) { // send it if full, or remaining bytes = buffer 72 | 73 | if (!_headerSent) Send_Header(200, "text/json"); 74 | 75 | _client.write(_buffer + 0, _bufferCount); 76 | _size -= _bufferCount; // keep track of remaining bytes.. 77 | _bufferCount = 0; // reset the buffer to begining.. 78 | } 79 | break; 80 | 81 | } // end of switch 82 | 83 | return true; 84 | 85 | } // end of write 86 | 87 | void HTTPPrinter::SetHeader(int code, const char* content) { 88 | if (code < 0) code = 0; 89 | _code = code; 90 | _headerContent = content; 91 | } 92 | 93 | 94 | size_t HTTPPrinter::Send(WiFiClient client, int code, const char* content, printer_callback fn ){ 95 | 96 | Begin(client); 97 | SetHeader(code, content); 98 | SetCountMode(true); 99 | fn(); 100 | size_t size = SetCountMode(false); 101 | fn(); 102 | End(); 103 | 104 | client.stop(); 105 | while(client.connected()) yield(); 106 | 107 | return size; 108 | } 109 | 110 | void HTTPPrinter::Send_Header () { 111 | 112 | Send_Header(_code, _headerContent); 113 | SetCountMode(false); 114 | 115 | 116 | } 117 | 118 | size_t HTTPPrinter::Send_Buffer(int code, const char* content) { 119 | 120 | _size = _bufferCount; 121 | Send_Header(code, content); 122 | _client.write(_buffer + 0, _size); 123 | //Serial.write(_buffer+0, _size); 124 | return _size; 125 | End(); 126 | 127 | } 128 | 129 | 130 | 131 | void HTTPPrinter::Send_Header (int code, const char * content) { 132 | 133 | if (_headerSent) return; 134 | 135 | uint8_t *headerBuff = (uint8_t*)malloc(128); 136 | sprintf((char*)headerBuff, "HTTP/1.1 %u OK\r\nContent-Type: %s\r\nContent-Length: %u\r\nConnection: close\r\nAccess-Control-Allow-Origin: *\r\n\r\n", code, content,_size); 137 | // Serial.println(); 138 | // Serial.println((char*)headerBuff); 139 | 140 | size_t headerLen = strlen((const char*)headerBuff); 141 | _client.write((const uint8_t*)headerBuff, headerLen); 142 | free(headerBuff); 143 | _headerSent = true; 144 | 145 | } 146 | 147 | -------------------------------------------------------------------------------- /HTTPPrinter.h: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------- 2 | This lib is designed to stream out data to wificlient, in chunks. 3 | 4 | Designed to circumvent the issue of unknown size. Send the data you want through it 5 | after setting SetCountMode = true, once fininshed set it to false. Now when 6 | you start to send data, content length is set to this value in header. 7 | 8 | This 9 | method is very fast, much faster than waiting for the client to disconnect. 10 | 80K, of totally dynamic JSON, made on the fly, can be created and sent in 330ms. 11 | 12 | The buffer size defaults to HTTPPrinterSize, but can be changed at initialisation, 13 | for example HTTPPrinter.Begin (c, 500); will create a 500 byte send buffer. 14 | c, is a WiFiClient object reference , typically if you use 15 | c = (your HTTP instance).client(); 16 | 17 | Setcountmode returns size of the buffer at that time. 18 | 19 | Send_Buffer allows you to send whatever is in the buffer as long as it is below your 20 | HTTPPrinterSize value. usefull if you don't need to stream through the buffer twice. 21 | 22 | A cool use case is in the ESP8266-Hue lib. here... functions are created that just print 23 | to this printer. printer.Send( _HTTP->client() , 200, "text/json", Send_Config_Callback ); 24 | this then takes care of size by running the function Send_Config_Callback twice... 25 | 26 | Inspiration for design of this lib came from me-no-dev and probonopd. 27 | 28 | --------------------------------------------------------------------*/ 29 | 30 | #pragma once 31 | 32 | #include "Arduino.h" 33 | #include 34 | #include 35 | 36 | 37 | #define HTTPPrinterSize 1000 // Sets the default size of buffer/packet to send out. 38 | 39 | 40 | class HTTPPrinter: public Print 41 | { 42 | 43 | public: 44 | 45 | typedef std::function printer_callback; 46 | 47 | HTTPPrinter(); 48 | void Begin(WiFiClient & c); 49 | void Begin(WiFiClient & c, size_t memorysize); 50 | void End(); 51 | void Setsize(size_t s); 52 | size_t GetSize(); 53 | size_t SetCountMode(bool mode); 54 | 55 | size_t Send(WiFiClient client, int code, const char* content, printer_callback fn ); // uses printer callback 56 | size_t Send_Buffer(int code, const char* content); // just sends out the buffer as is... 57 | 58 | void SetHeader(int code, const char* content); 59 | void Send_Header (); 60 | 61 | private: 62 | virtual size_t write(uint8_t); 63 | 64 | void Send_Header (int code, const char * content ); 65 | uint8_t *_buffer; 66 | int _bufferCount; 67 | WiFiClient _client; 68 | size_t _size, _sizeTotal; 69 | bool _headerSent; 70 | bool _CountMode; 71 | int _code; 72 | const char* _headerContent; 73 | }; 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /HueBridge-example.ino: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------------ 2 | Example Sketch for Phillips Hue 3 | 4 | This lib requires you to have the neopixelbus lib in your arduino libs folder, 5 | for the colour management header files & . 6 | https://github.com/Makuna/NeoPixelBus/tree/UartDriven 7 | *** Needs to be UARTDRIVEN branch, or Animator Branch *** 8 | 9 | 10 | ------------------------------------------------------------------------------*/ 11 | 12 | 13 | 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "HueBridge.h" 25 | #include // NeoPixelAnimator branch 26 | 27 | 28 | const char* host = "Hue-Bridge"; 29 | const char* ssid = "SKY"; 30 | const char* pass = "wellcometrust"; 31 | const uint16_t aport = 8266; 32 | 33 | WiFiServer TelnetServer(aport); 34 | WiFiClient Telnet; 35 | WiFiUDP OTA; 36 | 37 | ESP8266WebServer HTTP(80); 38 | 39 | //HueBridge* Hue = NULL; // used for dynamic memory allocation... 40 | 41 | #define pixelCount 250 // Strip has 30 NeoPixels 42 | #define pixelPin 2 // Strip is attached to GPIO2 on ESP-01 43 | #define HUEgroups 5 44 | #define HUElights 10 45 | 46 | HueBridge Hue(&HTTP, HUElights, HUEgroups, &HandleHue); 47 | 48 | NeoPixelBus strip = NeoPixelBus(pixelCount, pixelPin); 49 | 50 | NeoPixelAnimator animator(&strip); // NeoPixel animation management object 51 | 52 | 53 | void HandleHue(uint8_t Lightno, struct RgbColor rgb, HueLight* lightdata) { 54 | 55 | // Hue callback.... 56 | 57 | // Serial.printf( "\n | Light = %u, Name = %s (%u,%u,%u) | ", Lightno, lightdata->Name, lightdata->Hue, lightdata->Sat, lightdata->Bri); 58 | //temporarily holds data from vals 59 | char x[10]; 60 | char y[10]; 61 | //4 is mininum width, 3 is precision; float value is copied onto buff 62 | dtostrf(lightdata->xy.x, 5, 4, x); 63 | dtostrf(lightdata->xy.y, 5, 4, y); 64 | 65 | 66 | Serial.printf( " | light = %3u, %15s , RGB(%3u,%3u,%3u), HSB(%5u,%3u,%3u), XY(%s,%s),mode=%s | \n", 67 | Lightno, lightdata->Name, rgb.R, rgb.G, rgb.B, lightdata->hsb.H, lightdata->hsb.S, lightdata->hsb.B, x, y, lightdata->Colormode); 68 | 69 | 70 | Lightno--; 71 | RgbColor original = strip.GetPixelColor(Lightno); 72 | 73 | AnimUpdateCallback animUpdate = [=](float progress) 74 | { 75 | RgbColor updatedColor; 76 | 77 | if (lightdata->State) { 78 | updatedColor = RgbColor::LinearBlend(original, rgb, progress); 79 | } else { 80 | updatedColor = RgbColor::LinearBlend(original, RgbColor(0,0,0), progress); 81 | } 82 | 83 | strip.SetPixelColor(Lightno, updatedColor); 84 | }; 85 | 86 | animator.StartAnimation(Lightno, 1000, animUpdate); 87 | 88 | } 89 | 90 | 91 | 92 | void setup() { 93 | strip.Begin(); 94 | strip.Show(); 95 | 96 | Serial.begin(115200); 97 | 98 | delay(500); 99 | 100 | Serial.println(""); 101 | Serial.println("Arduino HueBridge Test"); 102 | 103 | Serial.printf("Sketch size: %u\n", ESP.getSketchSize()); 104 | Serial.printf("Free size: %u\n", ESP.getFreeSketchSpace()); 105 | 106 | uint8_t i = 0; 107 | 108 | while (WiFi.status() != WL_CONNECTED ) { 109 | delay(500); 110 | i++; 111 | Serial.print("."); 112 | if (i == 20) { 113 | Serial.print("Failed"); 114 | ESP.reset(); 115 | break; 116 | } ; 117 | } 118 | 119 | 120 | if(WiFi.waitForConnectResult() == WL_CONNECTED){ 121 | 122 | 123 | MDNS.begin(host); 124 | MDNS.addService("arduino", "tcp", aport); 125 | OTA.begin(aport); 126 | TelnetServer.begin(); 127 | TelnetServer.setNoDelay(true); 128 | delay(10); 129 | Serial.print("\nIP address: "); 130 | String IP = String(WiFi.localIP()); 131 | //Serial.println(IP); 132 | Serial.println(WiFi.localIP()); 133 | 134 | Hue.Begin(); 135 | 136 | HTTP.begin(); 137 | 138 | } else { 139 | Serial.println("CONFIG FAILED... rebooting"); 140 | ESP.reset(); 141 | } 142 | 143 | Serial.print("Free Heap: "); 144 | Serial.println(ESP.getFreeHeap()); 145 | 146 | } 147 | 148 | 149 | void loop() { 150 | 151 | HTTP.handleClient(); 152 | yield(); 153 | 154 | OTA_handle(); 155 | 156 | static unsigned long update_strip_time = 0; // keeps track of pixel refresh rate... limits updates to 33 Hz 157 | 158 | if (millis() - update_strip_time > 30) 159 | { 160 | if ( animator.IsAnimating() ) animator.UpdateAnimations(100); 161 | strip.Show(); 162 | update_strip_time = millis(); 163 | } 164 | 165 | 166 | } 167 | 168 | 169 | 170 | void OTA_handle(){ 171 | 172 | if (OTA.parsePacket()) { 173 | IPAddress remote = OTA.remoteIP(); 174 | int cmd = OTA.parseInt(); 175 | int port = OTA.parseInt(); 176 | int size = OTA.parseInt(); 177 | 178 | Serial.print("Update Start: ip:"); 179 | Serial.print(remote); 180 | Serial.printf(", port:%d, size:%d\n", port, size); 181 | uint32_t startTime = millis(); 182 | 183 | WiFiUDP::stopAll(); 184 | 185 | if(!Update.begin(size)){ 186 | Serial.println("Update Begin Error"); 187 | return; 188 | } 189 | 190 | WiFiClient client; 191 | 192 | bool connected = false; 193 | 194 | delay(2000); 195 | 196 | //do { 197 | connected = client.connect(remote, port); 198 | //} while (!connected || millis() - startTime < 20000); 199 | 200 | if (connected) { 201 | 202 | uint32_t written; 203 | while(!Update.isFinished()){ 204 | written = Update.write(client); 205 | if(written > 0) client.print(written, DEC); 206 | } 207 | Serial.setDebugOutput(false); 208 | 209 | if(Update.end()){ 210 | client.println("OK"); 211 | Serial.printf("Update Success: %u\nRebooting...\n", millis() - startTime); 212 | ESP.restart(); 213 | } else { 214 | Update.printError(client); 215 | Update.printError(Serial); 216 | } 217 | 218 | 219 | } else { 220 | Serial.printf("Connect Failed: %u\n", millis() - startTime); 221 | ESP.restart(); 222 | } 223 | } 224 | //IDE Monitor (connected to Serial) 225 | if (TelnetServer.hasClient()){ 226 | if (!Telnet || !Telnet.connected()){ 227 | if(Telnet) Telnet.stop(); 228 | Telnet = TelnetServer.available(); 229 | } else { 230 | WiFiClient toKill = TelnetServer.available(); 231 | toKill.stop(); 232 | } 233 | } 234 | if (Telnet && Telnet.connected() && Telnet.available()){ 235 | while(Telnet.available()) 236 | Serial.write(Telnet.read()); 237 | } 238 | if(Serial.available()){ 239 | size_t len = Serial.available(); 240 | uint8_t * sbuf = (uint8_t *)malloc(len); 241 | Serial.readBytes(sbuf, len); 242 | if (Telnet && Telnet.connected()){ 243 | Telnet.write((uint8_t *)sbuf, len); 244 | yield(); 245 | } 246 | free(sbuf); 247 | } 248 | //delay(1); 249 | } -------------------------------------------------------------------------------- /HueBridge.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "HueBridge.h" 3 | 4 | HueBridge::HueBridge(ESP8266WebServer * HTTP, uint8_t lights, uint8_t groups, HueBridge::HueHandlerFunctionNEW fn): 5 | _HTTP(HTTP), 6 | _LightCount(lights), 7 | _GroupCount(groups), 8 | // _Handler(fn), 9 | _HandlerNEW(fn), 10 | _returnJSON(true), 11 | // _RunningGroupCount(2) 12 | _nextfreegroup(0) 13 | 14 | { 15 | if (_GroupCount < 2) _GroupCount = 2; // minimum required 16 | 17 | initHUE(_LightCount, _GroupCount); // creates the data structures... 18 | 19 | 20 | } 21 | 22 | HueBridge::~HueBridge() { 23 | 24 | if (Lights) delete[] Lights; 25 | if (Groups) delete[] Groups; 26 | 27 | Lights = NULL; 28 | Groups = NULL; // Must check these.... 29 | 30 | _HTTP->onNotFound(NULL); 31 | } 32 | 33 | void HueBridge::Begin() { 34 | 35 | _macString = String(WiFi.macAddress()); // toDo turn to 4 byte array.. 36 | _ipString = StringIPaddress(WiFi.localIP()); 37 | _netmaskString = StringIPaddress(WiFi.subnetMask()); 38 | _gatewayString = StringIPaddress(WiFi.gatewayIP()); 39 | 40 | initSSDP(); 41 | 42 | _HTTP->onNotFound(std::bind(&HueBridge::HandleWebRequest, this)); // register the onNotFound for Webserver to HueBridge 43 | _HTTP->serveStatic("/hue/lights.conf", SPIFFS , "/lights.conf"); 44 | _HTTP->serveStatic("/hue/groups.conf", SPIFFS , "/groups.conf"); 45 | _HTTP->serveStatic("/hue/test.conf", SPIFFS , "/test.conf"); 46 | _HTTP->on("/hue/format", [this]() { 47 | SPIFFS.format(); 48 | _HTTP->send ( 200, "text/plain", "DONE" ); 49 | } ); 50 | 51 | Serial.println(F("Philips Hue Started....")); 52 | Serial.print(F("Size Lights = ")); 53 | Serial.println(sizeof(HueLight) * _LightCount); 54 | Serial.print(F("Size Groups = ")); 55 | Serial.println(sizeof(HueGroup) * _GroupCount); 56 | 57 | SPIFFS.begin(); 58 | /*------------------------------------------------------------------------------------------------ 59 | 60 | Import saved Lights from SPIFFS 61 | 62 | ------------------------------------------------------------------------------------------------*/ 63 | File LightsFile = SPIFFS.open("/lights.conf", "r"); 64 | 65 | if (!LightsFile) 66 | { 67 | Serial.println(F("Failed to open lights.conf")); 68 | } else { 69 | Serial.println(F("Opened lights.conf")); 70 | Serial.print(F("CONFIG FILE CONTENTS----------------------")); 71 | Serial.println(); 72 | 73 | uint8_t light[sizeof(HueLight)]; 74 | size_t size = LightsFile.size(); 75 | uint8_t counter = 0; 76 | 77 | for(int i=0; iName, thelight->State, thelight->hsb.H, thelight->hsb.S, thelight->hsb.B); 89 | 90 | counter++; 91 | } 92 | 93 | LightsFile.close(); 94 | } 95 | /*------------------------------------------------------------------------------------------------ 96 | 97 | Import saved Groups from SPIFFS 98 | 99 | 100 | ------------------------------------------------------------------------------------------------*/ 101 | File GroupsFile = SPIFFS.open("/groups.conf", "r"); 102 | 103 | if (!GroupsFile) 104 | { 105 | Serial.println(F("Failed to open groups.conf")); 106 | } else { 107 | 108 | Serial.println(F("Opened groups.conf")); 109 | Serial.print(F("CONFIG FILE CONTENTS----------------------")); 110 | Serial.println(); 111 | 112 | uint8_t group[sizeof(HueGroup)]; 113 | size_t size = GroupsFile.size(); 114 | uint8_t counter = 0; 115 | 116 | for(int i=0; iName, thegroup->Inuse, thegroup->State, thegroup->Hue, thegroup->Sat, thegroup->Bri); 127 | counter++; 128 | } 129 | 130 | GroupsFile.close(); 131 | } 132 | 133 | 134 | _nextfreegroup = find_nextfreegroup(); 135 | 136 | } 137 | 138 | void HueBridge::initHUE(uint8_t Lightcount, uint8_t Groupcount) { 139 | 140 | if (Lights) delete[] Lights; 141 | if (Groups) delete[] Groups; 142 | 143 | Lights = new HueLight[Lightcount]; 144 | Groups = new HueGroup[Groupcount]; 145 | 146 | for (uint8_t i = 0; i < Lightcount; i++) { 147 | HueLight* currentlight = &Lights[i]; 148 | sprintf(currentlight->Name, "Hue Light %u", i+1); 149 | } 150 | 151 | for (uint8_t i = 0; i < Groupcount; i++) { 152 | HueGroup *g = &Groups[i]; 153 | if ( i == 0 || i == 1) g->Inuse = true; // sets group 0 and 1 to always be in use... 154 | sprintf(g->Name, "Group %u", i); 155 | g->LightsCount = 0 ; 156 | 157 | for (uint8_t i = 0; i < MaxLightMembersPerGroup; i++) { 158 | //uint8_t randomlight = random(1,Lightcount + 1 ); 159 | g->LightMembers[i] = 0; 160 | } 161 | 162 | } 163 | 164 | } 165 | 166 | 167 | void HueBridge::HandleWebRequest() { 168 | 169 | //------------------------------------------------------ 170 | // Initial web request handles .... 171 | //------------------------------------------------------ 172 | HTTPMethod RequestMethod = _HTTP->method(); 173 | long startime = millis(); 174 | 175 | // Serial.print("Uri: "); 176 | // Serial.println(_HTTP->uri()); 177 | 178 | // if (_lastrequest > 0) { 179 | 180 | // long timer = millis() - _lastrequest; 181 | // Serial.print("TSLR = "); 182 | // Serial.print(timer); 183 | // Serial.print(" :"); 184 | // } 185 | // _lastrequest = millis(); 186 | ///////////////////////////////// 187 | 188 | 189 | //------------------------------------------------------ 190 | // Extract User 191 | //------------------------------------------------------ 192 | 193 | //Hue_Commands Command; // holds command issued by client 194 | 195 | 196 | if ( _HTTP->uri() != "/api" && _HTTP->uri().charAt(4) == '/' && _HTTP->uri() != "/api/config" ) { 197 | user = _HTTP->uri().substring(5, _HTTP->uri().length()); 198 | if (user.indexOf("/") > 0) { 199 | user = user.substring(0, user.indexOf("/")); 200 | } 201 | } 202 | 203 | //Serial.print("Session user = "); 204 | //Serial.println(user); 205 | 206 | isAuthorized = true; 207 | 208 | if (!isAuthorized) return; // exit if none authorised user... toDO... 209 | 210 | //------------------------------------------------------ 211 | // Determine command 212 | //------------------------------------------------------ 213 | 214 | // /api/JPnfsdoKSVacEA0f/lights/8 --> renames light 215 | //Serial.print(" "); 216 | size_t sent = 0; 217 | 218 | if ( _HTTP->uri() == "/description.xml") { 219 | 220 | Handle_Description(); 221 | return; 222 | 223 | } else if ( _HTTP->uri() == "/api" ) { 224 | Serial.println("CREATE_USER - toDO"); 225 | //Command = CREATE_USER; 226 | _HTTP->send(404); 227 | return; 228 | } else if ( _HTTP->uri().endsWith("/config") ) { 229 | 230 | //Command = GET_CONFIG; 231 | sent = printer.Send( _HTTP->client() , 200, "text/json", std::bind(&HueBridge::Send_Config_Callback, this) ); // Send_Config_Callback 232 | return; 233 | } else if ( _HTTP->uri() == "/api/" + user ) { 234 | //Command = GET_FULLSTATE; 235 | 236 | sent = printer.Send( _HTTP->client() , 200, "text/json", std::bind(&HueBridge::Send_DataStore_Callback, this) ); 237 | return; 238 | } else if ( _HTTP->uri().indexOf(user) != -1 && _HTTP->uri().endsWith("/state") ) { 239 | 240 | if (RequestMethod == HTTP_PUT) { 241 | //Command = PUT_LIGHT; 242 | //Serial.print("PUT_LIGHT"); 243 | Put_light(); 244 | 245 | } else if (RequestMethod == HTTP_GET) { 246 | //Command = GET_LIGHT; 247 | Serial.print("GET_LIGHT - toDo"); // ToDo 248 | _HTTP->send(404); 249 | 250 | } else { 251 | Serial.print("LIGHT Unknown req: "); 252 | Serial.print(HTTPMethod_text[RequestMethod]); 253 | _HTTP->send(404); 254 | 255 | } 256 | return; 257 | } else if ( _HTTP->uri().indexOf(user) != -1 && _HTTP->uri().indexOf("/groups/") != -1 ) { // old _HTTP->uri().endsWith("/action") 258 | 259 | if (RequestMethod == HTTP_PUT) { 260 | //Command = PUT_GROUP; 261 | //Serial.print("PUT_GROUP"); 262 | Put_group(); 263 | 264 | } else if (RequestMethod == HTTP_GET) { 265 | //Command = GET_GROUP; 266 | Serial.print("GET_GROUP- todo"); // ToDo 267 | _HTTP->send(404); 268 | 269 | } else { 270 | Serial.print("GROUP Unknown req: "); 271 | Serial.print(HTTPMethod_text[RequestMethod]); 272 | _HTTP->send(404); 273 | } 274 | return; 275 | } else if (_HTTP->uri().indexOf(user) != -1 && _HTTP->uri().endsWith("/groups") ) { 276 | 277 | if (RequestMethod == HTTP_PUT) { 278 | //Command = ADD_GROUP; 279 | Serial.println("ADD_GROUP"); 280 | Add_Group(); 281 | 282 | } 283 | 284 | 285 | return; 286 | 287 | } else if ( _HTTP->uri().substring(0, _HTTP->uri().lastIndexOf("/") ) == "/api/" + user + "/lights" ) { 288 | 289 | if (RequestMethod == HTTP_PUT) { 290 | //Command = PUT_LIGHT_ROOT; 291 | Serial.println("PUT_LIGHT_ROOT"); 292 | Put_Light_Root(); 293 | 294 | } else { 295 | //Command = GET_LIGHT_ROOT; 296 | Serial.println("GET_LIGHT_ROOT - todo"); 297 | _HTTP->send(404); 298 | } 299 | return; 300 | } else if ( _HTTP->uri() == "/api/" + user + "/lights" ) { 301 | //Command = GET_ALL_LIGHTS; 302 | Serial.println("GET_ALL_LIGHTS- todo"); 303 | _HTTP->send(404); 304 | return; 305 | } 306 | 307 | 308 | 309 | else 310 | 311 | { 312 | 313 | Serial.print("UnknownUri: "); 314 | Serial.print(_HTTP->uri()); 315 | Serial.print(" "); 316 | Serial.println(HTTPMethod_text[RequestMethod]); 317 | 318 | if (_HTTP->arg("plain") != "" ) { 319 | Serial.print("BODY: "); 320 | Serial.println(_HTTP->arg("plain")); 321 | } 322 | _HTTP->send(404); 323 | return; 324 | 325 | } 326 | 327 | //------------------------------------------------------ 328 | // END OF NEW command PARSING 329 | //------------------------------------------------------ 330 | 331 | 332 | // long _time = millis() - startime; 333 | // Serial.print(" "); 334 | // Serial.print(_time); 335 | // Serial.println("ms"); 336 | 337 | //Serial.println(); 338 | 339 | } 340 | 341 | 342 | 343 | /*------------------------------------------------------------------------------------------- 344 | 345 | Functions to send out JSON data 346 | 347 | -------------------------------------------------------------------------------------------*/ 348 | 349 | 350 | void HueBridge::Send_DataStore_Callback() { 351 | 352 | printer.print("{"); // START of JSON 353 | printer.print(F("\"lights\":{ ")); 354 | Print_Lights(); // Lights 355 | printer.print("},"); 356 | printer.print(F("\"config\":{ ")); 357 | Print_Config(); // Config 358 | printer.print("},"); 359 | printer.print(F("\"groups\": { ")); 360 | Print_Groups(); // Config 361 | printer.print(F("},")); 362 | printer.print(F("\"scenes\" : { }")); 363 | printer.print(","); 364 | printer.print(F("\"schedules\" : { }")); 365 | printer.print("}"); // END of JSON 366 | } 367 | 368 | 369 | void HueBridge::Send_Config_Callback () { 370 | 371 | printer.print("{"); // START of JSON 372 | Print_Config(); // Config 373 | printer.print("}"); // END of JSON 374 | 375 | } 376 | 377 | void HueBridge::Print_Lights() { 378 | 379 | for (uint8_t i = 0; i < _LightCount; i++) { 380 | 381 | if (i > 0 ) printer.print(","); 382 | 383 | uint8_t LightNumber = i + 1; 384 | HueLight* currentlight = &Lights[i]; 385 | 386 | printer.print(F("\"")); 387 | printer.print(LightNumber); 388 | printer.print(F("\":{")); 389 | printer.print(F("\"type\":\"Extended color light\",")); 390 | printer.printf("\"name\":\"%s\",",currentlight->Name); 391 | printer.print(F("\"modelid\":\"LST001\",")); 392 | printer.print(F("\"state\":{")); 393 | ( currentlight->State == true ) ? printer.print(F("\"on\":true,")) : printer.print(F("\"on\":false,")); 394 | printer.printf("\"bri\":%u,", currentlight->hsb.B); 395 | printer.printf("\"hue\":%u,", currentlight->hsb.H); 396 | printer.printf("\"sat\":%u,", currentlight->hsb.S); 397 | 398 | //temporarily holds data from vals 399 | char x[10]; 400 | char y[10]; 401 | // clear the array 402 | memset(x,0,10); 403 | memset(y,0,10); 404 | 405 | // check is a number 406 | // if isnan(currentlight->xy.x) currentlight->xy.x = 0; 407 | // if isnan(currentlight->xy.y) currentlight->xy.y = 0; 408 | //4 is mininum width, 3 is precision; float value is copied onto buf 409 | if (!isnan(currentlight->xy.x)) dtostrf(currentlight->xy.x, 5, 4, x); else x[0] = '0' ; 410 | if (!isnan(currentlight->xy.y)) dtostrf(currentlight->xy.y, 5, 4, y); else y[0] = '0' ; 411 | 412 | //dtostrf(currentlight->xy.y, 5, 4, y); 413 | 414 | printer.printf("\"xy\":[%s,%s],", x, y ); 415 | printer.printf("\"ct\":%u,", currentlight->Ct); 416 | printer.print(F("\"alert\":\"none\",")); 417 | printer.print(F("\"effect\":\"none\",")); 418 | printer.printf("\"colormode\":\"%s\",", currentlight->Colormode); 419 | printer.print(F("\"reachable\":true}")); 420 | printer.print("}"); 421 | } 422 | 423 | } 424 | 425 | void HueBridge::Print_Groups(){ 426 | 427 | uint8_t groups_sent = 0; 428 | 429 | for (uint8_t GroupNumber = 1; GroupNumber < _GroupCount; GroupNumber++) { // start at 1 to NOT send group 0.. 430 | 431 | HueGroup* currentgroup = &Groups[GroupNumber]; 432 | 433 | if ( currentgroup->Inuse || GroupNumber == _nextfreegroup ) { 434 | 435 | if (groups_sent > 0 ) printer.print(","); 436 | 437 | printer.print(F("\"")); 438 | printer.print(GroupNumber); 439 | printer.print(F("\":{")); 440 | printer.printf("\"name\":\"%s\",",currentgroup->Name); 441 | printer.print(F("\"lights\": [")); 442 | 443 | for (uint8_t j = 0; j < currentgroup->LightsCount; j++) { 444 | if (j>0) printer.print(","); 445 | printer.printf("\"%u\"", currentgroup->LightMembers[j]); 446 | 447 | } 448 | 449 | printer.print(F("],")); 450 | printer.print(F("\"type\":\"LightGroup\",")); 451 | printer.print(F("\"action\": {")); 452 | ( currentgroup->State == true ) ? printer.print(F("\"on\":true,")) : printer.print(F("\"on\":false,")); 453 | printer.printf("\"bri\":%u,", currentgroup->Bri); 454 | printer.printf("\"hue\":%u,", currentgroup->Hue); 455 | printer.printf("\"sat\":%u,", currentgroup->Sat); 456 | printer.print("\"effect\": \"none\","); 457 | 458 | //temporarily holds data from vals 459 | char x[10]; 460 | char y[10]; 461 | //4 is mininum width, 3 is precision; float value is copied onto buff 462 | dtostrf(currentgroup->xy.x, 5, 4, x); 463 | dtostrf(currentgroup->xy.y, 5, 4, y); 464 | 465 | printer.printf("\"xy\":[%s,%s],", x, y ); 466 | printer.printf("\"ct\":%u,", currentgroup->Ct); 467 | printer.print(F("\"alert\":\"none\",")); 468 | printer.printf("\"colormode\":\"%s\"", currentgroup->Colormode); 469 | printer.print("}}"); 470 | 471 | groups_sent++; // used for comma placement 472 | } 473 | 474 | } 475 | 476 | } 477 | 478 | 479 | void HueBridge::Print_Config() { 480 | 481 | printer.print("\"name\":\"Hue Emulator\","); 482 | printer.print("\"swversion\":\"0.1\","); 483 | printer.printf("\"mac\": \"%s\",", (char*)_macString.c_str() ); 484 | printer.print("\"dhcp\":true,"); 485 | printer.printf("\"ipaddress\": \"%s\",", (char*)_ipString.c_str() ); 486 | printer.printf("\"netmask\": \"%s\",", (char*)_netmaskString.c_str() ); 487 | printer.printf("\"gateway\": \"%s\",", (char*)_gatewayString.c_str() ); 488 | printer.print(F("\"swupdate\":{\"text\":\"\",\"notify\":false,\"updatestate\":0,\"url\":\"\"},")); 489 | printer.print(F("\"whitelist\":{\"xyz\":{\"name\":\"clientname#devicename\"}}")); 490 | 491 | } 492 | 493 | 494 | void HueBridge::SendJson(JsonObject& root){ 495 | 496 | size_t size = 0; // root.measureLength(); 497 | //Serial.print(" JSON "); 498 | WiFiClient c = _HTTP->client(); 499 | printer.Begin(c); 500 | printer.SetHeader(200, "text/json"); 501 | printer.SetCountMode(true); 502 | root.printTo(printer); 503 | size = printer.SetCountMode(false); 504 | root.printTo(printer); 505 | c.stop(); 506 | while(c.connected()) yield(); 507 | printer.End(); // free memory... 508 | //Serial.printf(" %uB\n", size ); 509 | 510 | } 511 | 512 | void HueBridge::SendJson(JsonArray& root){ 513 | 514 | 515 | size_t size = 0; 516 | //Serial.print(" JSON "); 517 | WiFiClient c = _HTTP->client(); 518 | printer.Begin(c); 519 | printer.SetHeader(200, "text/json"); 520 | printer.SetCountMode(true); 521 | root.printTo(printer); 522 | size = printer.SetCountMode(false); 523 | root.printTo(printer); 524 | c.stop(); 525 | while(c.connected()) yield(); 526 | printer.End(); // free memory... 527 | //Serial.printf(" %uB\n", size); 528 | 529 | } 530 | 531 | 532 | void HueBridge::Handle_Description() { 533 | 534 | String str = F("10http://"); 535 | str += _ipString; 536 | str += F(":80/urn:schemas-upnp-org:device:Basic:1Philips hue ("); 537 | str += _ipString; 538 | str += F(")Royal Philips Electronicshttp://www.philips.comPhilips hue Personal Wireless LightingPhilips hue bridge 2012929000226503http://www.meethue.com00178817122cuuid:2f402f80-da50-11e1-9b23-00178817122cindex.htmlimage/png484824hue_logo_0.pngimage/png12012024hue_logo_3.png"); 539 | _HTTP->send(200, "text/plain", str); 540 | Serial.println(F("/Description.xml SENT")); 541 | 542 | } 543 | 544 | 545 | /*------------------------------------------------------------------------------------------- 546 | 547 | Functions to process known web request PUT LIGHT 548 | 549 | -------------------------------------------------------------------------------------------*/ 550 | 551 | void HueBridge::Put_light () { 552 | 553 | if (_HTTP->arg("plain") == "") 554 | { 555 | return; 556 | } 557 | 558 | //------------------------------------------------------ 559 | // Extract Light ID ==> Map to LightNumber (toDo) 560 | //------------------------------------------------------ 561 | 562 | uint8_t numberOfTheLight = Extract_LightID(); 563 | String lightID = String(numberOfTheLight + 1); 564 | 565 | HueLight* currentlight = &Lights[numberOfTheLight]; 566 | 567 | struct RgbColor rgb; 568 | 569 | //------------------------------------------------------ 570 | // JSON set up IN + OUT 571 | //------------------------------------------------------ 572 | 573 | DynamicJsonBuffer jsonBufferOUT; // create buffer 574 | DynamicJsonBuffer jsonBufferIN; // create buffer 575 | 576 | JsonObject& root = jsonBufferIN.parseObject(_HTTP->arg("plain")); 577 | JsonArray& array = jsonBufferOUT.createArray(); // new method 578 | 579 | //------------------------------------------------------ 580 | // ON / OFF 581 | //------------------------------------------------------ 582 | 583 | bool onValue{false}, hasHue{false}, hasBri{false}, hasSat{false}, hasXy{false}; 584 | 585 | // struct HueHSB &hsb = currentlight->hsb; // might be useful method for later. 586 | 587 | 588 | 589 | 590 | 591 | 592 | if (root.containsKey("on")) 593 | { 594 | onValue = root["on"]; 595 | 596 | String response = F("/lights/"); response += lightID ; response += F("/state/on"); // + onoff; 597 | 598 | if (onValue) { 599 | currentlight->State = true; 600 | 601 | if (strcmp (currentlight->Colormode,"hs") == 0 ) { 602 | 603 | rgb = HUEhsb2rgb(currentlight->hsb); // designed to handle philips hue RGB ALLOCATED FROM JSON REQUEST 604 | 605 | } else if (strcmp (currentlight->Colormode,"xy") == 0) { 606 | rgb = XYtorgb(currentlight->xy, currentlight->hsb.B); // set the color to return 607 | } 608 | 609 | if (_returnJSON) AddSucessToArray (array, response, F("on") ); 610 | 611 | } else { 612 | 613 | currentlight->State = false; 614 | rgb = RgbColor(0,0,0); 615 | if (_returnJSON) AddSucessToArray (array, response, F("off") ) ; 616 | 617 | } 618 | 619 | } 620 | 621 | //------------------------------------------------------ 622 | // HUE / SAT / BRI 623 | //------------------------------------------------------ 624 | 625 | if (root.containsKey("hue")) 626 | { 627 | currentlight->hsb.H = root["hue"]; 628 | String response = "/lights/" + lightID + "/state/hue"; // + onoff; 629 | hasHue = true; 630 | if (_returnJSON) AddSucessToArray (array, response, root["hue"] ); 631 | } 632 | 633 | if (root.containsKey("sat")) 634 | { 635 | currentlight->hsb.S = root["sat"]; 636 | String response = "/lights/" + lightID + "/state/sat"; // + onoff; 637 | hasSat = true; 638 | if (_returnJSON) AddSucessToArray (array, response, root["sat"]); 639 | 640 | } 641 | 642 | if (root.containsKey("bri")) 643 | { 644 | currentlight->hsb.B = root["bri"]; 645 | String response = "/lights/" + lightID + "/state/bri"; // + onoff; 646 | hasBri = true; 647 | if (_returnJSON) AddSucessToArray (array, response, root["bri"]); 648 | } 649 | 650 | //------------------------------------------------------ 651 | // XY Color Space 652 | //------------------------------------------------------ 653 | 654 | 655 | 656 | if (root.containsKey("xy")) 657 | { 658 | currentlight->xy.x = root["xy"][0]; 659 | currentlight->xy.y = root["xy"][1]; 660 | hasXy = true; 661 | 662 | if (_returnJSON) { 663 | JsonObject& nestedObject = array.createNestedObject(); 664 | JsonObject& sucess = nestedObject.createNestedObject("success"); 665 | JsonArray& xyObject = sucess.createNestedArray("xy"); 666 | xyObject.add(currentlight->xy.x, 4); // 4 digits: "3.1415" 667 | xyObject.add(currentlight->xy.x, 4); // 4 digits: "3.1415" 668 | } 669 | } 670 | 671 | //------------------------------------------------------ 672 | // Ct Colour space 673 | //------------------------------------------------------ 674 | 675 | if (root.containsKey("ct")) 676 | { 677 | 678 | // todo 679 | 680 | 681 | } 682 | 683 | //------------------------------------------------------ 684 | // Apply recieved Color data 685 | //------------------------------------------------------ 686 | 687 | if (hasHue || hasBri || hasSat) { 688 | rgb = HUEhsb2rgb(currentlight->hsb); // designed to handle philips hue RGB ALLOCATED FROM JSON REQUEST 689 | if (!hasXy) currentlight->xy = HUEhsb2xy(currentlight->hsb); // COPYED TO LED STATE incase onother applciation requests colour 690 | strcpy(currentlight->Colormode, "hs"); 691 | 692 | } 693 | 694 | if (hasXy) { 695 | rgb = XYtorgb(currentlight->xy, currentlight->hsb.B); // set the color to return 696 | currentlight->hsb = xy2HUEhsb(currentlight->xy, currentlight->hsb.B); // converts for storage... 697 | strcpy(currentlight->Colormode, "xy"); 698 | } 699 | 700 | 701 | //------------------------------------------------------ 702 | // SEND Back changed JSON REPLY 703 | //------------------------------------------------------ 704 | 705 | // Serial.println(); 706 | // array.prettyPrintTo(Serial); 707 | // Serial.println(); 708 | 709 | if (_returnJSON) SendJson(array); 710 | 711 | //printer.print("]"); 712 | //printer.Send_Buffer(200,"text/json"); 713 | //------------------------------------- 714 | // Print Saved State Buffer to serial 715 | //------------------------------------------------------ 716 | 717 | // Serial.print("Saved = Hue:"); 718 | // Serial.print(StripHueData[numberOfTheLight].Hue); 719 | // Serial.print(", Sat:"); 720 | // Serial.print(StripHueData[numberOfTheLight].Sat); 721 | // Serial.print(", Bri:"); 722 | // Serial.print(StripHueData[numberOfTheLight].Bri); 723 | // Serial.println(); 724 | 725 | 726 | //------------------------------------------------------ 727 | // Set up LEDs... rock and roll.... 728 | //------------------------------------------------------ 729 | 730 | _HandlerNEW(numberOfTheLight + 1, rgb, currentlight); 731 | 732 | //------------------------------------------------------ 733 | // Save Settings to SPIFFS 734 | //------------------------------------------------------ 735 | 736 | long start_time_spiffs = millis(); 737 | 738 | File configFile = SPIFFS.open("/lights.conf", "r+"); 739 | 740 | if (!configFile) 741 | { 742 | Serial.println(F("Failed to open test.conf, making new")); 743 | configFile = SPIFFS.open("/lights.conf", "w+"); 744 | 745 | } else { 746 | 747 | Serial.println(F("Opened Hue_conf.txt for UPDATE....")); 748 | 749 | unsigned char * data = reinterpret_cast(Lights); // use unsigned char, as uint8_t is not guarunteed to be same width as char... 750 | size_t bytes = configFile.write(data, sizeof(HueLight) * _LightCount ); // C++ way 751 | 752 | configFile.close(); 753 | Serial.print("TIME TAKEN: "); 754 | Serial.print(millis() - start_time_spiffs); 755 | Serial.print("ms, "); 756 | Serial.print(bytes); 757 | Serial.println("B"); 758 | } 759 | 760 | /*--------------------------------------------------------------------------------------------------------------------------------------- 761 | Experimental - Per Light saving of settings.. move towards file system based storage, for all hue data. 762 | This is working.. saves only the current light to SPIFFS... 763 | //---------------------------------------------------------------------------------------------------------------------------------------*/ 764 | 765 | 766 | #ifdef EXPERIMENTAL 767 | 768 | File testFile; 769 | 770 | long t1 = micros(); 771 | long h1 = ESP.getFreeHeap(); 772 | 773 | testFile = SPIFFS.open("/test.conf", "r+"); 774 | 775 | File lightFile = SPIFFS.open("/lights.conf", "r+"); 776 | File groupFile = SPIFFS.open("/groups.conf", "r+"); 777 | 778 | 779 | Serial.printf("Open file time: %uus, heap use = %u \n", micros() - t1 , h1 - ESP.getFreeHeap()); 780 | 781 | long start_spiffs = micros(); 782 | 783 | if (!testFile) { 784 | testFile = SPIFFS.open("/test.conf", "w+"); 785 | } 786 | 787 | if (testFile) { 788 | 789 | uint32_t position = numberOfTheLight * sizeof(HueLight); 790 | 791 | if(!testFile.seek(position, SeekSet)) { // if seek fails... ie file is too short 792 | testFile.seek(0, SeekEnd); // go to end of file 793 | do { 794 | testFile.write(0); // write 0 until in right place... 795 | } while (testFile.position() < position); 796 | } 797 | 798 | unsigned char * data = reinterpret_cast(currentlight); // C++ way 799 | size_t bytes = testFile.write(data, sizeof(HueLight)); 800 | 801 | Serial.printf("newFS Time taken %uus, %uB \n", micros() - start_spiffs, bytes); 802 | 803 | 804 | long t2 = micros(); 805 | long h2 = ESP.getFreeHeap(); 806 | 807 | testFile.close(); 808 | 809 | Serial.printf("Open close time: %uus, heap use = %u \n", micros() - t2, ESP.getFreeHeap() - h2); 810 | 811 | } else { 812 | Serial.println("FAILED TO WRITE TO + CREATE FILE"); 813 | } 814 | #endif 815 | 816 | } 817 | 818 | /*------------------------------------------------------------------------------------------- 819 | 820 | Functions to process known web request PUT GROUP 821 | 822 | -------------------------------------------------------------------------------------------*/ 823 | 824 | void HueBridge::Put_group () { 825 | 826 | if (_HTTP->arg("plain") == "") 827 | { 828 | return; 829 | } 830 | 831 | // Serial.print("\nREQUEST: "); 832 | // Serial.println(_HTTP->uri()); 833 | // Serial.println(_HTTP->arg("plain")); 834 | 835 | 836 | //------------------------------------------------------ 837 | // Extract Light ID ==> Map to LightNumber (toDo) 838 | //------------------------------------------------------ 839 | 840 | //uint8_t numberOfTheGroup = Extract_LightID(); 841 | 842 | uint8_t groupNum = atoi(subStr(_HTTP->uri().c_str(), (char*) "/", 4)); // 843 | 844 | uint8_t numberOfTheGroup = groupNum; 845 | String groupID = String(groupNum); 846 | 847 | // Serial.print("\nGr = "); 848 | // Serial.print(groupID); 849 | // Serial.print(" _RunningGroupCount = "); 850 | // Serial.println(_RunningGroupCount); 851 | 852 | if (numberOfTheGroup > _GroupCount) return; 853 | 854 | HueGroup* currentgroup; 855 | currentgroup = &Groups[groupNum]; 856 | 857 | struct RgbColor rgb; 858 | struct HueHSB hsb; 859 | 860 | bool colourschanged = false; 861 | //------------------------------------------------------ 862 | // JSON set up IN + OUT 863 | //------------------------------------------------------ 864 | 865 | DynamicJsonBuffer jsonBufferOUT; // create buffer 866 | DynamicJsonBuffer jsonBufferIN; // create buffer 867 | JsonObject& root = jsonBufferIN.parseObject(_HTTP->arg("plain")); 868 | JsonArray& array = jsonBufferOUT.createArray(); // new method 869 | 870 | //------------------------------------------------------ 871 | // ON / OFF 872 | //------------------------------------------------------ 873 | 874 | bool onValue = false; 875 | 876 | if (root.containsKey("on")) 877 | { 878 | onValue = root["on"]; 879 | String response = "/groups/" + groupID + "/state/on"; // + onoff; 880 | 881 | if (onValue) { 882 | // Serial.print(" ON :"); 883 | currentgroup->State = true; 884 | if (_returnJSON) AddSucessToArray (array, response, F("on") ); 885 | 886 | } else { 887 | // Serial.print(" OFF :"); 888 | currentgroup->State = false; 889 | rgb = RgbColor(0,0,0); 890 | if (_returnJSON) AddSucessToArray (array, response, F("off") ) ; 891 | 892 | } 893 | colourschanged = true; 894 | 895 | } 896 | 897 | //------------------------------------------------------ 898 | // HUE / SAT / BRI 899 | //------------------------------------------------------ 900 | // To Do Colormode..... 901 | 902 | bool hasHue{false}, hasBri{false}, hasSat{false}; 903 | 904 | uint16_t hue = currentgroup->Hue ; 905 | uint8_t sat = currentgroup->Sat ; 906 | uint8_t bri = currentgroup->Bri ; 907 | 908 | if (root.containsKey("hue")) 909 | { 910 | hue = root["hue"]; 911 | String response = "/groups/" + groupID + "/state/hue"; // + onoff; 912 | // Serial.print(" HUE -> "); 913 | // Serial.print(hue); 914 | // Serial.print(", "); 915 | hasHue = true; 916 | if (_returnJSON) AddSucessToArray (array, response, root["hue"] ); 917 | } 918 | 919 | if (root.containsKey("sat")) 920 | { 921 | sat = root["sat"]; 922 | String response = "/groups/" + groupID + "/state/sat"; // + onoff; 923 | // Serial.print(" SAT -> "); 924 | // Serial.print(sat); 925 | // Serial.print(", "); 926 | hasSat = true; 927 | if (_returnJSON) AddSucessToArray (array, response, root["sat"]); 928 | } 929 | 930 | if (root.containsKey("bri")) 931 | { 932 | bri = root["bri"]; 933 | String response = "/groups/" + groupID + "/state/bri"; // + onoff; 934 | // Serial.print(" BRI -> "); 935 | // Serial.print(bri); 936 | // Serial.print(", "); 937 | hasBri = true; 938 | if (_returnJSON) AddSucessToArray (array, response, root["bri"]); 939 | } 940 | 941 | //------------------------------------------------------ 942 | // XY Color Space 943 | //------------------------------------------------------ 944 | 945 | HueXYColor xy_instance; 946 | bool hasXy{false}; 947 | 948 | if (root.containsKey("xy")) 949 | { 950 | xy_instance.x = root["xy"][0]; 951 | xy_instance.y = root["xy"][1]; 952 | currentgroup->xy = xy_instance; 953 | // Serial.print(" XY ("); 954 | // Serial.print(xy_instance.x); 955 | // Serial.print(","); 956 | // Serial.print(xy_instance.y); 957 | // Serial.print(") "); 958 | hasXy = true; 959 | if (_returnJSON) { 960 | JsonObject& nestedObject = array.createNestedObject(); 961 | JsonObject& sucess = nestedObject.createNestedObject("success"); 962 | JsonArray& xyObject = sucess.createNestedArray("xy"); 963 | xyObject.add(xy_instance.x, 4); // 4 digits: "3.1415" 964 | xyObject.add(xy_instance.y, 4); // 4 digits: "3.1415" 965 | } 966 | } 967 | 968 | //------------------------------------------------------ 969 | // Ct Colour space 970 | //------------------------------------------------------ 971 | 972 | if (root.containsKey("ct")) 973 | { 974 | 975 | 976 | 977 | 978 | } 979 | 980 | //------------------------------------------------------ 981 | // Apply recieved Color data 982 | //------------------------------------------------------ 983 | 984 | if (hasHue) currentgroup->Hue = hsb.H = hue; 985 | if (hasBri) currentgroup->Sat = hsb.S = sat; 986 | if (hasSat) currentgroup->Bri = hsb.B = bri; 987 | 988 | if (hasHue || hasSat || hasBri) { 989 | rgb = HUEhsb2rgb(hsb); // designed to handle philips hue RGB ALLOCATED FROM JSON REQUEST 990 | currentgroup->xy = HUEhsb2xy(hsb); // COPYED TO LED STATE incase onother applciation requests colour 991 | colourschanged = true; 992 | } else if (hasXy) { 993 | 994 | rgb = XYtorgb(xy_instance, bri); // set the color to return 995 | hsb = xy2HUEhsb(xy_instance, bri); // converts for storage... 996 | 997 | currentgroup->Hue = hsb.H; ///floor(hsb.H * 182.04 * 360.0); 998 | currentgroup->Sat = hsb.S; //floor(hsb.S * 254); 999 | currentgroup->Bri = hsb.B; // floor(hsb.B * 254); 1000 | colourschanged = true; 1001 | } 1002 | 1003 | 1004 | //------------------------------------------------------ 1005 | // Group NAME 1006 | //------------------------------------------------------ 1007 | 1008 | if (root.containsKey("name")) { 1009 | 1010 | Name_Group(groupNum, root["name"]); 1011 | String response = "/groups/" + groupID + "/name/"; // + onoff; 1012 | if (_returnJSON) AddSucessToArray (array, response, root["name"]); 1013 | 1014 | } 1015 | 1016 | //------------------------------------------------------ 1017 | // Group LIGHTS 1018 | //------------------------------------------------------ 1019 | 1020 | if (root.containsKey("lights")) { 1021 | 1022 | JsonArray& array2 = root["lights"]; 1023 | uint8_t i = 0; 1024 | 1025 | for(JsonArray::iterator it=array2.begin(); it!=array2.end(); ++it) 1026 | { 1027 | uint8_t value = atoi(it->as()); 1028 | if (i < MaxLightMembersPerGroup) { 1029 | currentgroup->LightMembers[i] = value; 1030 | } 1031 | i++; 1032 | } 1033 | 1034 | currentgroup->LightsCount = i; 1035 | if (i == 0 && groupNum > 1) currentgroup->Inuse = false; else currentgroup->Inuse = true; // added group number check so groups 0+1 are always in use.. 1036 | _nextfreegroup = find_nextfreegroup(); 1037 | } 1038 | 1039 | //------------------------------------------------------ 1040 | // SEND Back changed JSON REPLY 1041 | //------------------------------------------------------ 1042 | 1043 | // char *msgStr = aJson.print(reply); 1044 | // aJson.deleteItem(reply); 1045 | // Serial.print("\nJSON REPLY = "); 1046 | // //Serial.println(millis()); 1047 | // Serial.println(msgStr); 1048 | // HTTP.send(200, "text/plain", msgStr); 1049 | // free(msgStr); 1050 | 1051 | 1052 | // Serial.println("RESPONSE:"); 1053 | // array.prettyPrintTo(Serial); 1054 | // Serial.println(); 1055 | 1056 | if (_returnJSON) SendJson(array); 1057 | 1058 | 1059 | //------------------------------------------------------ 1060 | // Print Saved State Buffer to serial 1061 | //------------------------------------------------------ 1062 | 1063 | // Serial.print("Saved = Hue:"); 1064 | // Serial.print(StripHueData[numberOfTheLight].Hue); 1065 | // Serial.print(", Sat:"); 1066 | // Serial.print(StripHueData[numberOfTheLight].Sat); 1067 | // Serial.print(", Bri:"); 1068 | // Serial.print(StripHueData[numberOfTheLight].Bri); 1069 | // Serial.println(); 1070 | 1071 | 1072 | //------------------------------------------------------ 1073 | // Set up LEDs... rock and roll.... 1074 | //------------------------------------------------------ 1075 | 1076 | 1077 | uint8_t Group = (uint8_t)groupID.toInt(); 1078 | 1079 | // _Handler(Light, 2000, rgb); 1080 | 1081 | if (colourschanged) { 1082 | 1083 | if (Group == 0 ) { 1084 | 1085 | for (uint8_t i = 0; i < _LightCount; i++) { 1086 | 1087 | HueLight* currentlight; 1088 | currentlight = &Lights[i]; // values stored in array are Hue light numbers so + 1; 1089 | _HandlerNEW(i+1, rgb, currentlight); 1090 | // currentlight->Hue = currentgroup->Hue; 1091 | // currentlight->Sat = currentgroup->Sat; 1092 | // currentlight->Bri = currentgroup->Bri; 1093 | currentlight->xy = currentgroup->xy; 1094 | currentlight->State = currentgroup->State; 1095 | 1096 | } 1097 | 1098 | } else { 1099 | 1100 | for (uint8_t i = 0; i < currentgroup->LightsCount; i++) { 1101 | 1102 | HueLight* currentlight; 1103 | currentlight = &Lights[currentgroup->LightMembers[i] - 1]; // values stored in array are Hue light numbers so + 1; 1104 | _HandlerNEW(currentgroup->LightMembers[i], rgb, currentlight); 1105 | // currentlight->Hue = currentgroup->Hue; 1106 | // currentlight->Sat = currentgroup->Sat; 1107 | // currentlight->Bri = currentgroup->Bri; 1108 | currentlight->xy = currentgroup->xy; 1109 | currentlight->State = currentgroup->State; 1110 | 1111 | } 1112 | 1113 | } 1114 | 1115 | } 1116 | 1117 | 1118 | //------------------------------------------------------ 1119 | // Save Settings to SPIFFS 1120 | //------------------------------------------------------ 1121 | 1122 | long start_time_spiffs = millis(); 1123 | 1124 | File configFile = SPIFFS.open("/groups.conf", "w+"); 1125 | 1126 | if (!configFile) 1127 | { 1128 | Serial.println(F("Failed to open groups.conf")); 1129 | } else { 1130 | Serial.println(F("Opened groups.conf for UPDATE....")); 1131 | Serial.printf("Start Position =%u \n", configFile.position()); 1132 | 1133 | unsigned char * data = reinterpret_cast(Groups); // use unsigned char, as uint8_t is not guarunteed to be same width as char... 1134 | 1135 | size_t bytes = configFile.write(data, sizeof(HueGroup) * _GroupCount ); // C++ way 1136 | 1137 | Serial.printf("END Position =%u \n", configFile.position()); 1138 | 1139 | configFile.close(); 1140 | Serial.print("TIME TAKEN: "); 1141 | Serial.print(millis() - start_time_spiffs); 1142 | Serial.print("ms, "); 1143 | Serial.print(bytes); 1144 | Serial.println("B"); 1145 | } 1146 | 1147 | 1148 | 1149 | 1150 | 1151 | 1152 | 1153 | } 1154 | 1155 | 1156 | void HueBridge::Put_Light_Root() { 1157 | 1158 | //------------------------------------------------------ 1159 | // Set Light Name 1160 | //------------------------------------------------------ 1161 | DynamicJsonBuffer jsonBufferOUT; // create buffer 1162 | DynamicJsonBuffer jsonBufferIN; // create buffer 1163 | 1164 | 1165 | JsonObject& root = jsonBufferIN.parseObject( _HTTP->arg("plain")); 1166 | JsonArray& array = jsonBufferOUT.createArray(); // new method 1167 | 1168 | uint8_t numberOfTheLight = Extract_LightID(); 1169 | String lightID = String(numberOfTheLight + 1); 1170 | 1171 | if (root.containsKey("name")) 1172 | { 1173 | Name_Light(numberOfTheLight, root["name"]) ; 1174 | 1175 | String response = "/lights/" + lightID + "/name"; // + onoff; 1176 | AddSucessToArray (array, response, root["name"]); 1177 | } 1178 | 1179 | //array.prettyPrintTo(Serial); 1180 | 1181 | SendJson(array); 1182 | 1183 | } 1184 | 1185 | void HueBridge::Get_Light_Root() { 1186 | 1187 | //------------------------------------------------------ 1188 | // Get Light State 1189 | //------------------------------------------------------ 1190 | DynamicJsonBuffer jsonBufferOUT; // create buffer 1191 | JsonObject& object = jsonBufferOUT.createObject(); 1192 | 1193 | uint8_t LightID = Extract_LightID(); 1194 | //String numberOfTheLight = String(LightID + 1); 1195 | 1196 | // Add_light(object, LightID); 1197 | 1198 | SendJson(object); 1199 | 1200 | 1201 | } 1202 | 1203 | bool HueBridge::Add_Group() { 1204 | 1205 | //im not entirely sure that chroma uses this... 1206 | 1207 | Serial.println("ADD GROUP HIT"); 1208 | 1209 | if (_HTTP->arg("plain") == "") return 0; 1210 | if (_nextfreegroup == 0 ) return 0 ; // need to add sending error.... 1211 | 1212 | 1213 | HueGroup* currentgroup; 1214 | 1215 | for (uint8_t i = 0; i <= _GroupCount; i++){ 1216 | currentgroup = &Groups[i]; 1217 | if (!currentgroup->Inuse) break; 1218 | if (i == _GroupCount) return 0; // if they are all full return... 1219 | } 1220 | 1221 | 1222 | DynamicJsonBuffer jsonBufferOUT; // create buffer 1223 | DynamicJsonBuffer jsonBufferIN; // create buffer 1224 | 1225 | 1226 | JsonObject& root = jsonBufferIN.parseObject( _HTTP->arg("plain")); 1227 | JsonArray& array = jsonBufferOUT.createArray(); // new method 1228 | 1229 | //------------------------------------------------------ 1230 | // Group NAME 1231 | //------------------------------------------------------ 1232 | 1233 | if (root.containsKey("name")) { 1234 | 1235 | // Name_Group(_RunningGroupCount, root["name"]); 1236 | Name_Group(currentgroup, root["name"]); 1237 | 1238 | // String response = "/groups/" + groupID + "/name/"; // + onoff; 1239 | // if (_returnJSON) AddSucessToArray (array, response, root["name"]); 1240 | 1241 | } 1242 | 1243 | //------------------------------------------------------ 1244 | // Group LIGHTS 1245 | //------------------------------------------------------ 1246 | 1247 | if (root.containsKey("lights")) { 1248 | 1249 | //Serial.println(); 1250 | 1251 | JsonArray& array2 = root["lights"]; 1252 | 1253 | uint8_t i = 0; 1254 | 1255 | for(JsonArray::iterator it=array2.begin(); it!=array2.end(); ++it) 1256 | { 1257 | uint8_t value = atoi(it->as()); 1258 | 1259 | if (i < MaxLightMembersPerGroup) { 1260 | 1261 | currentgroup->LightMembers[i] = value; 1262 | 1263 | // Serial.print(i); 1264 | // Serial.print(" = "); 1265 | // Serial.println(value); 1266 | 1267 | } 1268 | 1269 | i++; 1270 | 1271 | } 1272 | 1273 | currentgroup->LightsCount = i; 1274 | 1275 | } 1276 | 1277 | 1278 | // _RunningGroupCount++; 1279 | 1280 | uint8_t freegroups = 0; 1281 | 1282 | for (uint8_t i = 0; i < _GroupCount; i++){ 1283 | HueGroup* currentgroup = &Groups[i]; 1284 | if (!currentgroup->Inuse) freegroups++; 1285 | } 1286 | 1287 | Serial.printf("Free groups remaining %u\n", freegroups); 1288 | 1289 | _nextfreegroup = find_nextfreegroup(); 1290 | 1291 | 1292 | } 1293 | 1294 | void HueBridge::Name_Light(uint8_t i, const char* name) { 1295 | 1296 | if (i > _LightCount) return; 1297 | HueLight* currentlight; 1298 | currentlight = &Lights[i]; 1299 | memset(currentlight->Name, 0, sizeof(currentlight->Name)); 1300 | //memcpy(StripHueData[i].name, name, sizeof(name) ); 1301 | strcpy(currentlight->Name, name ); 1302 | } 1303 | 1304 | void HueBridge::Name_Light(uint8_t i, String &name) { 1305 | 1306 | if (i > _LightCount) return; 1307 | HueLight* currentlight; 1308 | currentlight = &Lights[i]; 1309 | name.toCharArray(currentlight->Name, 32); 1310 | 1311 | } 1312 | 1313 | 1314 | 1315 | void HueBridge::Name_Group(HueGroup * currentgroup, const char* name) { 1316 | 1317 | //if (i > _GroupCount) return; 1318 | //HueGroup* currentgroup; 1319 | //currentgroup = &Groups[i]; 1320 | memset(currentgroup->Name, 0, sizeof(currentgroup->Name)); 1321 | //memcpy(StripHueData[i].name, name, sizeof(name) ); 1322 | strcpy(currentgroup->Name, name ); 1323 | } 1324 | 1325 | 1326 | 1327 | void HueBridge::Name_Group(uint8_t i, const char* name) { 1328 | 1329 | if (i > _GroupCount) return; 1330 | HueGroup* currentgroup; 1331 | currentgroup = &Groups[i]; 1332 | memset(currentgroup->Name, 0, sizeof(currentgroup->Name)); 1333 | //memcpy(StripHueData[i].name, name, sizeof(name) ); 1334 | strcpy(currentgroup->Name, name ); 1335 | } 1336 | 1337 | 1338 | // void HueBridge::Name_Group(uint8_t i, String &name) { 1339 | 1340 | // if (i > _GroupCount) return; 1341 | // HueGroup* currentgroup; 1342 | // currentgroup = &Groups[i]; 1343 | // name.toCharArray(currentgroup->Name, 32); 1344 | 1345 | // } 1346 | 1347 | uint8_t HueBridge::Extract_LightID() { 1348 | 1349 | String ID; 1350 | if (_HTTP->uri().indexOf("/lights/") > -1) { 1351 | ID = (_HTTP->uri().substring(_HTTP->uri().indexOf("/lights/") + 8 ,_HTTP->uri().indexOf("/state"))); 1352 | } else if (_HTTP->uri().indexOf("/groups/") > -1) { 1353 | ID = (_HTTP->uri().substring(_HTTP->uri().indexOf("/groups/") + 8 ,_HTTP->uri().indexOf("/action"))); 1354 | } else return 0; 1355 | 1356 | uint8_t IDint = ID.toInt() - 1; 1357 | //Serial.print(" LIGHT ID = "); 1358 | //Serial.println(lightID); 1359 | return IDint ; 1360 | } 1361 | 1362 | // New method using arduinoJSON 1363 | void HueBridge::AddSucessToArray(JsonArray& array, String item, String value) { 1364 | 1365 | JsonObject& nestedObject = array.createNestedObject(); 1366 | JsonObject& sucess = nestedObject.createNestedObject("success"); 1367 | sucess[item] = value; 1368 | 1369 | } 1370 | 1371 | void HueBridge::AddSucessToArray(JsonArray& array, String item, char* value) { 1372 | 1373 | JsonObject& nestedObject = array.createNestedObject(); 1374 | JsonObject& sucess = nestedObject.createNestedObject("success"); 1375 | sucess[item] = value; 1376 | 1377 | } 1378 | 1379 | String HueBridge::StringIPaddress(IPAddress myaddr) { 1380 | 1381 | 1382 | String LocalIP = ""; 1383 | for (int i = 0; i < 4; i++) 1384 | { 1385 | LocalIP += String(myaddr[i]); 1386 | if (i < 3) LocalIP += "."; 1387 | } 1388 | return LocalIP; 1389 | 1390 | } 1391 | 1392 | void HueBridge::initSSDP() { 1393 | 1394 | Serial.printf("Starting SSDP..."); 1395 | SSDP.begin(); 1396 | SSDP.setSchemaURL((char*)"description.xml"); 1397 | SSDP.setHTTPPort(80); 1398 | SSDP.setName((char*)"Philips hue clone"); 1399 | SSDP.setSerialNumber((char*)"001788102201"); 1400 | SSDP.setURL((char*)"index.html"); 1401 | SSDP.setModelName((char*)"Philips hue bridge 2012"); 1402 | SSDP.setModelNumber((char*)"929000226503"); 1403 | SSDP.setModelURL((char*)"http://www.meethue.com"); 1404 | SSDP.setManufacturer((char*)"Royal Philips Electronics"); 1405 | SSDP.setManufacturerURL((char*)"http://www.philips.com"); 1406 | Serial.println("SSDP Started"); 1407 | } 1408 | 1409 | void HueBridge::SetReply(bool value) { 1410 | _returnJSON = value; 1411 | } 1412 | 1413 | bool HueBridge::SetLightState(uint8_t light, bool value){ 1414 | 1415 | if(light == 0) return false; 1416 | light--; 1417 | if (light < 0 || light > _LightCount) return false; 1418 | HueLight* currentlight = &Lights[light]; 1419 | currentlight->State = value; 1420 | return true; 1421 | } 1422 | 1423 | bool HueBridge::GetLightState(uint8_t light) { 1424 | 1425 | if(light == 0) return false; 1426 | light--; 1427 | if (light < 0 || light > _LightCount) return false; 1428 | HueLight* currentlight = &Lights[light]; 1429 | return currentlight->State; 1430 | 1431 | } 1432 | 1433 | bool HueBridge::SetLightRGB(uint8_t light, RgbColor color) { 1434 | 1435 | // 1436 | // To Do 1437 | // 1438 | 1439 | } 1440 | 1441 | RgbColor HueBridge::GetLightRGB(uint8_t light) { 1442 | 1443 | 1444 | // 1445 | // To Do 1446 | // 1447 | 1448 | } 1449 | 1450 | 1451 | 1452 | 1453 | bool HueBridge::SetGroupState(uint8_t group, bool value){ 1454 | 1455 | if(group == 0) return false; 1456 | group--; 1457 | if (group < 0 || group > _GroupCount) return false; 1458 | HueGroup* currentgroup = &Groups[group]; 1459 | currentgroup->State = value; 1460 | return true; 1461 | } 1462 | 1463 | 1464 | 1465 | bool HueBridge::GetGroupState(uint8_t group) { 1466 | 1467 | if(group == 0) return false; 1468 | group--; 1469 | if (group < 0 || group > _GroupCount) return false; 1470 | HueGroup* currentgroup = &Groups[group]; 1471 | return currentgroup->State; 1472 | } 1473 | 1474 | 1475 | 1476 | 1477 | /*------------------------------------------------------------------------------------------- 1478 | 1479 | Colour Management 1480 | Thanks to probonopd 1481 | 1482 | -------------------------------------------------------------------------------------------*/ 1483 | 1484 | 1485 | struct HueHSB HueBridge::rgb2HUEhsb(RgbColor color) 1486 | { 1487 | 1488 | HsbColor hsb = HsbColor(color); 1489 | int hue, sat, bri; 1490 | 1491 | hue = floor(hsb.H * 182.04 * 360.0); 1492 | sat = floor(hsb.S * 254); 1493 | bri = floor(hsb.B * 254); 1494 | 1495 | HueHSB hsb2; 1496 | hsb2.H = hue; 1497 | hsb2.S = sat; 1498 | hsb2.B = bri; 1499 | 1500 | return (hsb2); 1501 | } 1502 | 1503 | 1504 | 1505 | struct RgbColor HueBridge::HUEhsb2rgb(HueHSB color) 1506 | { 1507 | 1508 | float H, S, B; 1509 | H = color.H / 182.04 / 360.0; 1510 | S = color.S / 254.0; 1511 | B = color.B / 254.0; 1512 | return HsbColor(H, S, B); 1513 | } 1514 | 1515 | struct HueXYColor HueBridge::rgb2xy(RgbColor color) { 1516 | 1517 | float red = float(color.R) / 255.0f; 1518 | float green = float(color.G) / 255.0f; 1519 | float blue = float(color.B) / 255.0f; 1520 | 1521 | // Wide gamut conversion D65 1522 | float r = ((red > 0.04045f) ? (float) pow((red + 0.055f) 1523 | / (1.0f + 0.055f), 2.4f) : (red / 12.92f)); 1524 | float g = (green > 0.04045f) ? (float) pow((green + 0.055f) 1525 | / (1.0f + 0.055f), 2.4f) : (green / 12.92f); 1526 | float b = (blue > 0.04045f) ? (float) pow((blue + 0.055f) 1527 | / (1.0f + 0.055f), 2.4f) : (blue / 12.92f); 1528 | 1529 | float x = r * 0.649926f + g * 0.103455f + b * 0.197109f; 1530 | float y = r * 0.234327f + g * 0.743075f + b * 0.022598f; 1531 | float z = r * 0.0000000f + g * 0.053077f + b * 1.035763f; 1532 | 1533 | // Calculate the xy values from the XYZ values 1534 | 1535 | HueXYColor xy_instance; 1536 | 1537 | xy_instance.x = x / (x + y + z); 1538 | xy_instance.y = y / (x + y + z); 1539 | 1540 | if (isnan(xy_instance.x) ) { 1541 | xy_instance.x = 0.0f; 1542 | } 1543 | if (isnan(xy_instance.y)) { 1544 | xy_instance.y = 0.0f; 1545 | } 1546 | 1547 | return xy_instance; 1548 | 1549 | 1550 | } 1551 | 1552 | struct HueXYColor HueBridge::HUEhsb2xy(HueHSB color) { 1553 | 1554 | RgbColor rgb = HUEhsb2rgb(color); 1555 | double r = rgb.R / 255.0; 1556 | double g = rgb.G / 255.0; 1557 | double b = rgb.B / 255.0; 1558 | double X = r * 0.649926f + g * 0.103455f + b *0.197109f; 1559 | double Y = r * 0.234327f + g * 0.743075f + b * 0.022598f; 1560 | double Z = r * 0.0000000f + g * 0.053077f + b * 1.035763f; 1561 | HueXYColor xy; 1562 | xy.x = X / (X + Y + Z); 1563 | xy.y = Y / (X + Y + Z); 1564 | return xy; 1565 | } 1566 | 1567 | struct HueHSB HueBridge::xy2HUEhsb(HueXYColor xy, uint8_t bri) { 1568 | 1569 | double x = xy.x; 1570 | double y = xy.y; 1571 | 1572 | double z = 1.0f - xy.x - xy.y; 1573 | double Y = (double)(bri / 254.0); // The given brightness value 1574 | double X = (Y / xy.y) * xy.x; 1575 | double Z = (Y / xy.y) * z; 1576 | double r = X * 1.4628067f - Y * 0.1840623f - Z * 0.2743606f; 1577 | double g = -X * 0.5217933f + Y* 1.4472381f + Z * 0.0677227f; 1578 | double b = X * 0.0349342f - Y * 0.0968930f + Z * 1.2884099f; 1579 | uint8_t R = abs(r) * 255; 1580 | uint8_t G = abs(g) * 255; 1581 | uint8_t B = abs(b) * 255; 1582 | struct HueHSB hsb; 1583 | 1584 | double mi, ma, delta, h; 1585 | mi = (RG)?R:G; 1586 | ma = (ma>B)?ma:B; 1587 | delta = ma - mi; 1588 | 1589 | if(ma <= 0.0){ 1590 | hsb.H = 0xFFFF; 1591 | hsb.S = 1; 1592 | hsb.B = bri; 1593 | return hsb; 1594 | } 1595 | 1596 | if (R >= ma) h = (G - B) / delta; // between yellow & magenta 1597 | else if(G >= ma) h = 2.0 + (B - R) / delta; // between cyan & yellow 1598 | else h = 4.0 + ( R - G ) / delta; // between magenta & cyan 1599 | h *= 60.0; // degrees 1600 | if(h < 0.0) h += 360.0; 1601 | hsb.H = (uint16_t)floor(h * 182.04); 1602 | hsb.S = (uint16_t)floor((delta / ma) * 254); 1603 | hsb.B = bri; 1604 | return hsb; 1605 | } 1606 | 1607 | // struct HueHSB ct2hsb(long kelvin, uint8_t bri) { 1608 | // double r, g, b; 1609 | // long temperature = kelvin / 10; 1610 | // if(temperature <= 66) { 1611 | // r = 255; 1612 | // } 1613 | // else { 1614 | // r = temperature - 60; 1615 | // r = 329.698727446 * pow(r, -0.1332047592); 1616 | // if(r < 0) r = 0; 1617 | // if(r > 255) r = 255; 1618 | // } 1619 | 1620 | // if(temperature <= 66) { 1621 | // g = temperature; 1622 | // g = 99.4708025861 log(g) - 161.1195681661; 1623 | // if(g < 0) g = 0; 1624 | // if(g > 255) g = 255; 1625 | // } 1626 | // else { 1627 | // g = temperature - 60; 1628 | // g = 288.1221695283 pow(g, -0.0755148492); 1629 | // if(g < 0) g = 0; 1630 | // if(g > 255) g = 255; 1631 | // } 1632 | 1633 | // if(temperature >= 66) { 1634 | // b = 255; 1635 | // } 1636 | // else { 1637 | // if(temperature <= 19) { 1638 | // b = 0; 1639 | // } 1640 | // else { 1641 | // b = temperature - 10; 1642 | // b = 138.5177312231 * log(b) - 305.0447927307; 1643 | // if(b < 0) b = 0; 1644 | // if(b > 255) b = 255; 1645 | // } 1646 | // } 1647 | 1648 | // uint8_t R = abs(r) 255; 1649 | // uint8_t G = abs(g) 255; 1650 | // uint8_t B = abs(b) * 255; 1651 | // struct HueHSB hsb; 1652 | // double mi, ma, delta, h; 1653 | // mi = (RG)?R:G; 1654 | // ma = (ma>B)?ma:B; 1655 | // delta = ma - mi; 1656 | // if(ma <= 0.0){ 1657 | // hsb.H = 0xFFFF; 1658 | // hsb.S = 1; 1659 | // hsb.B = bri; 1660 | // return hsb; 1661 | // } 1662 | 1663 | // if(R >= ma) h = (G - B) / delta; // between 1664 | // } 1665 | 1666 | //http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/ 1667 | // 'Given a temperature (in Kelvin), estimate an RGB equivalent 1668 | 1669 | struct RgbColor HueBridge::ct2rbg(long tmpKelvin, uint8_t bri) { 1670 | 1671 | if (tmpKelvin < 1000) tmpKelvin = 1000; 1672 | if (tmpKelvin > 40000)tmpKelvin = 40000; 1673 | 1674 | double tmpCalc; 1675 | RgbColor rgb; 1676 | tmpKelvin = tmpKelvin / 100; 1677 | 1678 | // RED. 1679 | if (tmpKelvin <= 66) { 1680 | rgb.R = 255; 1681 | } else { 1682 | tmpCalc = tmpKelvin - 60; 1683 | tmpCalc = 329.698727446 * pow(tmpCalc, -0.1332047592); 1684 | rgb.R = tmpCalc; 1685 | if (rgb.R < 0) rgb.R = 0; 1686 | if (rgb.R > 255) rgb.R = 255; 1687 | 1688 | } 1689 | // green 1690 | if (tmpKelvin <= 66) { 1691 | // 'Note: the R-squared value for this approximation is .996 1692 | tmpCalc = tmpKelvin; 1693 | tmpCalc = 99.4708025861 * log(tmpCalc) - 161.1195681661; 1694 | rgb.G = tmpCalc; 1695 | if (rgb.G < 0) rgb.G = 0; 1696 | if (rgb.G > 255) rgb.G = 255; 1697 | } else { 1698 | // 'Note: the R-squared value for this approximation is .987 1699 | tmpCalc = tmpKelvin - 60; 1700 | tmpCalc = 288.1221695283 * pow(tmpCalc,-0.0755148492); 1701 | rgb.G = tmpCalc; 1702 | if (rgb.G < 0) rgb.G = 0; 1703 | if (rgb.G > 255) rgb.G = 255; 1704 | } 1705 | 1706 | return rgb; 1707 | 1708 | 1709 | } 1710 | 1711 | struct HueHSB HueBridge::ct2hsb(long tmpKelvin, uint8_t bri) { 1712 | 1713 | RgbColor rgb = ct2rbg(tmpKelvin, bri); 1714 | return (rgb2HUEhsb(rgb)); 1715 | } 1716 | 1717 | 1718 | struct HueXYColor HueBridge::Ct2xy(long tmpKelvin, uint8_t bri) { 1719 | 1720 | RgbColor rgb = ct2rbg(tmpKelvin, bri); 1721 | return rgb2xy(rgb); 1722 | 1723 | } 1724 | 1725 | struct RgbColor HueBridge::XYtorgb(struct HueXYColor xy, uint8_t bri) { 1726 | 1727 | HueHSB hsb = xy2HUEhsb(xy,bri); 1728 | return HUEhsb2rgb(hsb); 1729 | 1730 | } 1731 | 1732 | 1733 | // Function to return a substring defined by a delimiter at an index 1734 | // From http://forum.arduino.cc/index.php?topic=41389.msg301116#msg301116 1735 | char* HueBridge::subStr(const char* str, char *delim, int index) { 1736 | char *act, *sub, *ptr; 1737 | static char copy[128]; // Length defines the maximum length of the c_string we can process 1738 | int i; 1739 | strcpy(copy, str); // Since strtok consumes the first arg, make a copy 1740 | for (i = 1, act = copy; i <= index; i++, act = NULL) { 1741 | sub = strtok_r(act, delim, &ptr); 1742 | if (sub == NULL) break; 1743 | } 1744 | return sub; 1745 | } 1746 | 1747 | 1748 | uint8_t HueBridge::find_nextfreegroup() { 1749 | 1750 | HueGroup* currentgroup = &Groups[_nextfreegroup]; 1751 | 1752 | if (!currentgroup->Inuse) return _nextfreegroup; 1753 | 1754 | for (uint8_t i = 0; i < _GroupCount; i++){ 1755 | currentgroup = &Groups[i]; 1756 | if (!currentgroup->Inuse) return i; 1757 | } 1758 | 1759 | return 0; 1760 | 1761 | } 1762 | 1763 | 1764 | 1765 | 1766 | -------------------------------------------------------------------------------- /HueBridge.h: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------- 2 | HueBridge for Arduino ESP8266 3 | Andrew Melvin - Sticilface 4 | 5 | 6 | Inspiration for design of this lib came from me-no-dev and probonopd. 7 | 8 | todo 9 | 10 | FIX nan for xy colour convertion.. breaks the JSON... 11 | Add /format for spiffs... 12 | user management 13 | save settings - kind of done. groups not done properly. just saves all to SPIFFS 14 | Testing of colour conversions + optimising/fixing them. 15 | Delete groups/ then adding them -> finding first free group slot.. etc... 16 | Shift over to SPIFFS filesystem, for everything, so whole array is not kept in RAM! 17 | 18 | *** 19 | Think about adding a function callback to a light... that can be called on on/off change or colour. 20 | maybe as an override to the generic... ie.. if blah != NULL then.... do this function instead... 21 | Could allow lights to do different things. such as call an MQTT function / UDP function. then the bridge can act as a bridge. 22 | *** 23 | 24 | 25 | // max groups in hue is 16 per bridge... 26 | --------------------------------------------------------------------*/ 27 | 28 | #pragma once 29 | 30 | #include "Arduino.h" 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | // These are from the neopixelbus lib. https://github.com/Makuna/NeoPixelBus/tree/UartDriven 41 | // Needs to be UARTDRIVEN branch, or Animator Branch 42 | 43 | #include 44 | #include 45 | 46 | #define MaxLightMembersPerGroup 10 // limits the size of the array holding lights in each group. 47 | 48 | 49 | #include "HueBridgeStructs.h" 50 | #include "HTTPPrinter.h" 51 | 52 | #define cache ICACHE_FLASH_ATTR // not being used yet... 53 | 54 | //#define EXPERIMENTAL // stuff for me to play with... 55 | 56 | static const char *HTTPMethod_text[] = { "HTTP_ANY", "HTTP_GET", "HTTP_POST", "HTTP_PUT", "HTTP_PATCH", "HTTP_DELETE" }; 57 | 58 | class RgbColor; 59 | class HsbColor; 60 | 61 | //typedef std::function HueHandlerFunction; 62 | 63 | 64 | class HueBridge 65 | { 66 | public: 67 | 68 | typedef std::function HueHandlerFunctionNEW; 69 | typedef std::function& GenericFunction; 70 | 71 | HueBridge(ESP8266WebServer * HTTP, uint8_t lights, uint8_t groups, HueHandlerFunctionNEW fn); 72 | ~HueBridge(); 73 | 74 | void Begin(); 75 | 76 | void SetReply(bool value); 77 | 78 | bool SetLightState(uint8_t light, bool value); 79 | bool GetLightState(uint8_t light); 80 | 81 | bool SetLightRGB(uint8_t light, RgbColor color); //ToDo 82 | struct RgbColor GetLightRGB(uint8_t light); //ToDo 83 | 84 | bool GetGroupState(uint8_t group); //ToDo 85 | bool SetGroupState(uint8_t group, bool value); //ToDo 86 | 87 | 88 | void SetGroupRGB(uint8_t group, uint8_t R, uint8_t G, uint8_t B); //ToDo 89 | void GetGroupRGB(uint8_t group); //ToDo 90 | 91 | void Get_Light_Root(); // NOT IMPLEMENTED 92 | void Put_Light_Root(); 93 | 94 | void Name_Light(uint8_t i, const char* name); 95 | void Name_Light(uint8_t i, String &name); 96 | void Name_Group(uint8_t i, const char* name); 97 | void Name_Group(HueGroup * currentgroup, const char* name); 98 | 99 | 100 | void Name_Group(uint8_t i, String &name); 101 | 102 | bool Add_Group(); 103 | 104 | struct HueHSB rgb2HUEhsb(struct RgbColor color); 105 | struct HueHSB xy2HUEhsb(struct HueXYColor xy, uint8_t bri); 106 | struct HueHSB ct2hsb(long tmpKelvin, uint8_t bri); 107 | 108 | struct RgbColor HUEhsb2rgb(HueHSB color); 109 | struct RgbColor XYtorgb(struct HueXYColor xy, uint8_t bri); 110 | struct RgbColor ct2rbg(long tmpKelvin, uint8_t bri); 111 | 112 | 113 | struct HueXYColor rgb2xy(struct RgbColor color); 114 | struct HueXYColor HUEhsb2xy(struct HueHSB color); 115 | struct HueXYColor Ct2xy(long tmpKelvin, uint8_t bri); 116 | 117 | 118 | private: 119 | 120 | void Send_DataStore_Callback(); 121 | void Send_Config_Callback(); 122 | void Print_Lights(); 123 | void Print_Groups(); 124 | void Print_Config(); 125 | void SendJson(JsonObject& root); 126 | void SendJson(JsonArray& root); 127 | void Handle_Description(); 128 | void Send_HTTPprinter(int code, const char* content, GenericFunction Fn); 129 | void Put_light(); 130 | void Put_group(); 131 | uint8_t Extract_LightID(); 132 | void AddSucessToArray(JsonArray& array, String item, String value); 133 | void AddSucessToArray(JsonArray& array, String item, char* value); 134 | void initSSDP(); 135 | void HandleWebRequest(); 136 | char* subStr(const char* str, char *delim, int index); 137 | void initHUE(uint8_t Lightcount, uint8_t Groupcount); 138 | uint8_t find_nextfreegroup(); 139 | 140 | 141 | String StringIPaddress(IPAddress myaddr); 142 | 143 | HTTPPrinter printer; 144 | ESP8266WebServer* _HTTP; 145 | WiFiClient _client; 146 | 147 | uint8_t _LightCount, _GroupCount, _nextfreegroup; 148 | 149 | String user; 150 | String _macString; 151 | String _ipString; 152 | String _netmaskString; 153 | String _gatewayString; 154 | bool isAuthorized, _returnJSON; 155 | 156 | long _lastrequest = 0; 157 | 158 | HueLight * Lights = NULL; 159 | HueGroup * Groups = NULL; 160 | 161 | // HueHandlerFunction _Handler; 162 | HueHandlerFunctionNEW _HandlerNEW; 163 | 164 | //enum Hue_Commands { NO = 0, CREATE_USER, GET_CONFIG, GET_FULLSTATE, GET_LIGHT, GET_NEW_LIGHTS, PUT_LIGHT, 165 | // GET_ALL_LIGHTS, PUT_LIGHT_ROOT, GET_LIGHT_ROOT, PUT_GROUP, GET_GROUP, ADD_GROUP }; 166 | 167 | 168 | }; -------------------------------------------------------------------------------- /HueBridgeStructs.h: -------------------------------------------------------------------------------- 1 | // to experiment with __attribute__((packed)) 2 | 3 | 4 | struct HueXYColor { 5 | float x{0.3127f}; 6 | float y{0.3290f}; 7 | }; 8 | 9 | struct HueHSB { 10 | uint16_t H{65280}; 11 | uint8_t S{1}; 12 | uint8_t B{254}; 13 | }; 14 | 15 | struct HueLight { 16 | char Name[32]; 17 | HueHSB hsb; 18 | uint16_t Ct{200}; 19 | HueXYColor xy; 20 | bool State{false}; 21 | uint16_t Transitiontime{10}; 22 | char Colormode[3]{'h','s','\0'}; // "hs" = hue, "ct" = colour temp, "xy" = xy 23 | }; 24 | 25 | 26 | struct HueGroup { 27 | char Name[32]; 28 | uint16_t Hue{65280}; 29 | uint8_t Sat{1}; 30 | uint8_t Bri{254}; 31 | uint16_t Ct{200}; 32 | HueXYColor xy; 33 | bool State{false}; 34 | uint8_t LightMembers[MaxLightMembersPerGroup]; 35 | uint8_t LightsCount{0}; 36 | char Colormode[3]{'h','s','\0'}; // "hs" = hue, "ct" = colour temp, "xy" = xy 37 | //char Colormode[3]; // "hs" = hue, "ct" = colour temp, "xy" = xy 38 | 39 | bool Inuse{0}; 40 | 41 | }; 42 | 43 | 44 | -------------------------------------------------------------------------------- /example of massive json.txt: -------------------------------------------------------------------------------- 1 | EXAMPLE MASSIVE JSON PRODUCED 2 | 3 | {"lights":{ "1":{"type":"Extended color light","name":"Hue Light 1","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"2":{"type":"Extended color light","name":"Hue Light 2","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"3":{"type":"Extended color light","name":"Hue Light 3","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"4":{"type":"Extended color light","name":"Hue Light 4","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"5":{"type":"Extended color light","name":"Hue Light 5","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"6":{"type":"Extended color light","name":"Hue Light 6","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"7":{"type":"Extended color light","name":"Hue Light 7","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"8":{"type":"Extended color light","name":"Hue Light 8","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"9":{"type":"Extended color light","name":"Hue Light 9","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"10":{"type":"Extended color light","name":"Hue Light 10","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"11":{"type":"Extended color light","name":"Hue Light 11","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"12":{"type":"Extended color light","name":"Hue Light 12","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"13":{"type":"Extended color light","name":"Hue Light 13","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"14":{"type":"Extended color light","name":"Hue Light 14","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"15":{"type":"Extended color light","name":"Hue Light 15","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"16":{"type":"Extended color light","name":"Hue Light 16","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"17":{"type":"Extended color light","name":"Hue Light 17","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"18":{"type":"Extended color light","name":"Hue Light 18","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"19":{"type":"Extended color light","name":"Hue Light 19","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"20":{"type":"Extended color light","name":"Hue Light 20","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"21":{"type":"Extended color light","name":"Hue Light 21","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"22":{"type":"Extended color light","name":"Hue Light 22","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"23":{"type":"Extended color light","name":"Hue Light 23","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"24":{"type":"Extended color light","name":"Hue Light 24","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"25":{"type":"Extended color light","name":"Hue Light 25","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"26":{"type":"Extended color light","name":"Hue Light 26","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"27":{"type":"Extended color light","name":"Hue Light 27","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"28":{"type":"Extended color light","name":"Hue Light 28","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"29":{"type":"Extended color light","name":"Hue Light 29","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"30":{"type":"Extended color light","name":"Hue Light 30","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"31":{"type":"Extended color light","name":"Hue Light 31","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"32":{"type":"Extended color light","name":"Hue Light 32","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"33":{"type":"Extended color light","name":"Hue Light 33","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"34":{"type":"Extended color light","name":"Hue Light 34","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"35":{"type":"Extended color light","name":"Hue Light 35","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"36":{"type":"Extended color light","name":"Hue Light 36","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"37":{"type":"Extended color light","name":"Hue Light 37","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"38":{"type":"Extended color light","name":"Hue Light 38","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"39":{"type":"Extended color light","name":"Hue Light 39","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"40":{"type":"Extended color light","name":"Hue Light 40","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"41":{"type":"Extended color light","name":"Hue Light 41","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"42":{"type":"Extended color light","name":"Hue Light 42","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"43":{"type":"Extended color light","name":"Hue Light 43","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"44":{"type":"Extended color light","name":"Hue Light 44","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"45":{"type":"Extended color light","name":"Hue Light 45","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"46":{"type":"Extended color light","name":"Hue Light 46","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"47":{"type":"Extended color light","name":"Hue Light 47","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"48":{"type":"Extended color light","name":"Hue Light 48","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"49":{"type":"Extended color light","name":"Hue Light 49","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"50":{"type":"Extended color light","name":"Hue Light 50","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"51":{"type":"Extended color light","name":"Hue Light 51","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"52":{"type":"Extended color light","name":"Hue Light 52","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"53":{"type":"Extended color light","name":"Hue Light 53","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"54":{"type":"Extended color light","name":"Hue Light 54","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"55":{"type":"Extended color light","name":"Hue Light 55","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"56":{"type":"Extended color light","name":"Hue Light 56","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"57":{"type":"Extended color light","name":"Hue Light 57","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"58":{"type":"Extended color light","name":"Hue Light 58","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"59":{"type":"Extended color light","name":"Hue Light 59","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"60":{"type":"Extended color light","name":"Hue Light 60","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"61":{"type":"Extended color light","name":"Hue Light 61","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"62":{"type":"Extended color light","name":"Hue Light 62","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"63":{"type":"Extended color light","name":"Hue Light 63","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"64":{"type":"Extended color light","name":"Hue Light 64","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"65":{"type":"Extended color light","name":"Hue Light 65","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"66":{"type":"Extended color light","name":"Hue Light 66","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"67":{"type":"Extended color light","name":"Hue Light 67","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"68":{"type":"Extended color light","name":"Hue Light 68","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"69":{"type":"Extended color light","name":"Hue Light 69","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"70":{"type":"Extended color light","name":"Hue Light 70","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"71":{"type":"Extended color light","name":"Hue Light 71","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"72":{"type":"Extended color light","name":"Hue Light 72","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"73":{"type":"Extended color light","name":"Hue Light 73","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"74":{"type":"Extended color light","name":"Hue Light 74","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"75":{"type":"Extended color light","name":"Hue Light 75","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"76":{"type":"Extended color light","name":"Hue Light 76","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"77":{"type":"Extended color light","name":"Hue Light 77","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"78":{"type":"Extended color light","name":"Hue Light 78","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"79":{"type":"Extended color light","name":"Hue Light 79","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"80":{"type":"Extended color light","name":"Hue Light 80","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"81":{"type":"Extended color light","name":"Hue Light 81","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"82":{"type":"Extended color light","name":"Hue Light 82","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"83":{"type":"Extended color light","name":"Hue Light 83","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"84":{"type":"Extended color light","name":"Hue Light 84","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"85":{"type":"Extended color light","name":"Hue Light 85","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"86":{"type":"Extended color light","name":"Hue Light 86","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"87":{"type":"Extended color light","name":"Hue Light 87","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"88":{"type":"Extended color light","name":"Hue Light 88","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"89":{"type":"Extended color light","name":"Hue Light 89","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"90":{"type":"Extended color light","name":"Hue Light 90","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"91":{"type":"Extended color light","name":"Hue Light 91","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"92":{"type":"Extended color light","name":"Hue Light 92","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"93":{"type":"Extended color light","name":"Hue Light 93","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"94":{"type":"Extended color light","name":"Hue Light 94","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"95":{"type":"Extended color light","name":"Hue Light 95","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"96":{"type":"Extended color light","name":"Hue Light 96","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"97":{"type":"Extended color light","name":"Hue Light 97","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"98":{"type":"Extended color light","name":"Hue Light 98","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"99":{"type":"Extended color light","name":"Hue Light 99","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"100":{"type":"Extended color light","name":"Hue Light 100","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"101":{"type":"Extended color light","name":"Hue Light 101","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"102":{"type":"Extended color light","name":"Hue Light 102","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"103":{"type":"Extended color light","name":"Hue Light 103","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"104":{"type":"Extended color light","name":"Hue Light 104","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"105":{"type":"Extended color light","name":"Hue Light 105","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"106":{"type":"Extended color light","name":"Hue Light 106","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"107":{"type":"Extended color light","name":"Hue Light 107","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"108":{"type":"Extended color light","name":"Hue Light 108","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"109":{"type":"Extended color light","name":"Hue Light 109","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"110":{"type":"Extended color light","name":"Hue Light 110","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"111":{"type":"Extended color light","name":"Hue Light 111","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"112":{"type":"Extended color light","name":"Hue Light 112","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"113":{"type":"Extended color light","name":"Hue Light 113","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"114":{"type":"Extended color light","name":"Hue Light 114","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"115":{"type":"Extended color light","name":"Hue Light 115","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"116":{"type":"Extended color light","name":"Hue Light 116","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"117":{"type":"Extended color light","name":"Hue Light 117","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"118":{"type":"Extended color light","name":"Hue Light 118","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"119":{"type":"Extended color light","name":"Hue Light 119","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"120":{"type":"Extended color light","name":"Hue Light 120","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"121":{"type":"Extended color light","name":"Hue Light 121","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"122":{"type":"Extended color light","name":"Hue Light 122","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"123":{"type":"Extended color light","name":"Hue Light 123","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"124":{"type":"Extended color light","name":"Hue Light 124","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"125":{"type":"Extended color light","name":"Hue Light 125","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"126":{"type":"Extended color light","name":"Hue Light 126","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"127":{"type":"Extended color light","name":"Hue Light 127","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"128":{"type":"Extended color light","name":"Hue Light 128","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"129":{"type":"Extended color light","name":"Hue Light 129","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"130":{"type":"Extended color light","name":"Hue Light 130","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"131":{"type":"Extended color light","name":"Hue Light 131","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"132":{"type":"Extended color light","name":"Hue Light 132","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"133":{"type":"Extended color light","name":"Hue Light 133","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"134":{"type":"Extended color light","name":"Hue Light 134","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"135":{"type":"Extended color light","name":"Hue Light 135","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"136":{"type":"Extended color light","name":"Hue Light 136","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"137":{"type":"Extended color light","name":"Hue Light 137","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"138":{"type":"Extended color light","name":"Hue Light 138","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"139":{"type":"Extended color light","name":"Hue Light 139","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"140":{"type":"Extended color light","name":"Hue Light 140","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"141":{"type":"Extended color light","name":"Hue Light 141","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"142":{"type":"Extended color light","name":"Hue Light 142","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"143":{"type":"Extended color light","name":"Hue Light 143","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"144":{"type":"Extended color light","name":"Hue Light 144","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"145":{"type":"Extended color light","name":"Hue Light 145","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"146":{"type":"Extended color light","name":"Hue Light 146","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"147":{"type":"Extended color light","name":"Hue Light 147","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"148":{"type":"Extended color light","name":"Hue Light 148","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"149":{"type":"Extended color light","name":"Hue Light 149","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"150":{"type":"Extended color light","name":"Hue Light 150","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"151":{"type":"Extended color light","name":"Hue Light 151","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"152":{"type":"Extended color light","name":"Hue Light 152","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"153":{"type":"Extended color light","name":"Hue Light 153","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"154":{"type":"Extended color light","name":"Hue Light 154","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"155":{"type":"Extended color light","name":"Hue Light 155","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"156":{"type":"Extended color light","name":"Hue Light 156","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"157":{"type":"Extended color light","name":"Hue Light 157","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"158":{"type":"Extended color light","name":"Hue Light 158","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"159":{"type":"Extended color light","name":"Hue Light 159","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"160":{"type":"Extended color light","name":"Hue Light 160","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"161":{"type":"Extended color light","name":"Hue Light 161","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"162":{"type":"Extended color light","name":"Hue Light 162","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"163":{"type":"Extended color light","name":"Hue Light 163","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"164":{"type":"Extended color light","name":"Hue Light 164","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"165":{"type":"Extended color light","name":"Hue Light 165","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"166":{"type":"Extended color light","name":"Hue Light 166","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"167":{"type":"Extended color light","name":"Hue Light 167","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"168":{"type":"Extended color light","name":"Hue Light 168","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"169":{"type":"Extended color light","name":"Hue Light 169","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"170":{"type":"Extended color light","name":"Hue Light 170","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"171":{"type":"Extended color light","name":"Hue Light 171","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"172":{"type":"Extended color light","name":"Hue Light 172","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"173":{"type":"Extended color light","name":"Hue Light 173","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"174":{"type":"Extended color light","name":"Hue Light 174","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"175":{"type":"Extended color light","name":"Hue Light 175","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"176":{"type":"Extended color light","name":"Hue Light 176","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"177":{"type":"Extended color light","name":"Hue Light 177","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"178":{"type":"Extended color light","name":"Hue Light 178","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"179":{"type":"Extended color light","name":"Hue Light 179","modelid":"LST001","state":{"on":true,"bri":254,"hue":10740,"sat":51,"xy":[0.3273,0.3570],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"180":{"type":"Extended color light","name":"Hue Light 180","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"181":{"type":"Extended color light","name":"Hue Light 181","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"182":{"type":"Extended color light","name":"Hue Light 182","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"183":{"type":"Extended color light","name":"Hue Light 183","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"184":{"type":"Extended color light","name":"Hue Light 184","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"185":{"type":"Extended color light","name":"Hue Light 185","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"186":{"type":"Extended color light","name":"Hue Light 186","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"187":{"type":"Extended color light","name":"Hue Light 187","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"188":{"type":"Extended color light","name":"Hue Light 188","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"189":{"type":"Extended color light","name":"Hue Light 189","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"190":{"type":"Extended color light","name":"Hue Light 190","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"191":{"type":"Extended color light","name":"Hue Light 191","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"192":{"type":"Extended color light","name":"Hue Light 192","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"193":{"type":"Extended color light","name":"Hue Light 193","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"194":{"type":"Extended color light","name":"Hue Light 194","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"195":{"type":"Extended color light","name":"Hue Light 195","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"196":{"type":"Extended color light","name":"Hue Light 196","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"197":{"type":"Extended color light","name":"Hue Light 197","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"198":{"type":"Extended color light","name":"Hue Light 198","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"199":{"type":"Extended color light","name":"Hue Light 199","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"200":{"type":"Extended color light","name":"Hue Light 200","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"201":{"type":"Extended color light","name":"Hue Light 201","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"202":{"type":"Extended color light","name":"Hue Light 202","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"203":{"type":"Extended color light","name":"Hue Light 203","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"204":{"type":"Extended color light","name":"Hue Light 204","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"205":{"type":"Extended color light","name":"Hue Light 205","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"206":{"type":"Extended color light","name":"Hue Light 206","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"207":{"type":"Extended color light","name":"Hue Light 207","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"208":{"type":"Extended color light","name":"Hue Light 208","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"209":{"type":"Extended color light","name":"Hue Light 209","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"210":{"type":"Extended color light","name":"Hue Light 210","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"211":{"type":"Extended color light","name":"Hue Light 211","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"212":{"type":"Extended color light","name":"Hue Light 212","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"213":{"type":"Extended color light","name":"Hue Light 213","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"214":{"type":"Extended color light","name":"Hue Light 214","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"215":{"type":"Extended color light","name":"Hue Light 215","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"216":{"type":"Extended color light","name":"Hue Light 216","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"217":{"type":"Extended color light","name":"Hue Light 217","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"218":{"type":"Extended color light","name":"Hue Light 218","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"219":{"type":"Extended color light","name":"Hue Light 219","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"220":{"type":"Extended color light","name":"Hue Light 220","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"221":{"type":"Extended color light","name":"Hue Light 221","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"222":{"type":"Extended color light","name":"Hue Light 222","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"223":{"type":"Extended color light","name":"Hue Light 223","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"224":{"type":"Extended color light","name":"Hue Light 224","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"225":{"type":"Extended color light","name":"Hue Light 225","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"226":{"type":"Extended color light","name":"Hue Light 226","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"227":{"type":"Extended color light","name":"Hue Light 227","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"228":{"type":"Extended color light","name":"Hue Light 228","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"229":{"type":"Extended color light","name":"Hue Light 229","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"230":{"type":"Extended color light","name":"Hue Light 230","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"231":{"type":"Extended color light","name":"Hue Light 231","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"232":{"type":"Extended color light","name":"Hue Light 232","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"233":{"type":"Extended color light","name":"Hue Light 233","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"234":{"type":"Extended color light","name":"Hue Light 234","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"235":{"type":"Extended color light","name":"Hue Light 235","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"236":{"type":"Extended color light","name":"Hue Light 236","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"237":{"type":"Extended color light","name":"Hue Light 237","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"238":{"type":"Extended color light","name":"Hue Light 238","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"239":{"type":"Extended color light","name":"Hue Light 239","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"240":{"type":"Extended color light","name":"Hue Light 240","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"241":{"type":"Extended color light","name":"Hue Light 241","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"242":{"type":"Extended color light","name":"Hue Light 242","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"243":{"type":"Extended color light","name":"Hue Light 243","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"244":{"type":"Extended color light","name":"Hue Light 244","modelid":"LST001","state":{"on":true,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"245":{"type":"Extended color light","name":"Hue Light 245","modelid":"LST001","state":{"on":true,"bri":254,"hue":23717,"sat":226,"xy":[0.1708,0.5840],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"246":{"type":"Extended color light","name":"Hue Light 246","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"247":{"type":"Extended color light","name":"Hue Light 247","modelid":"LST001","state":{"on":true,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"248":{"type":"Extended color light","name":"Hue Light 248","modelid":"LST001","state":{"on":false,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"249":{"type":"Extended color light","name":"Hue Light 249","modelid":"LST001","state":{"on":true,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}},"250":{"type":"Extended color light","name":"Hue Light 250","modelid":"LST001","state":{"on":true,"bri":254,"hue":65280,"sat":1,"xy":[0.3127,0.3290],"ct":200,"alert":"none","effect":"none","colormode":"hs","reachable":true}}},"config":{ "name":"Hue Emulator","swversion":"0.1","mac": "18:FE:34:A4:4C:73","dhcp":true,"ipaddress": "192.168.1.207","netmask": "255.255.255.0","gateway": "192.168.1.1","swupdate":{"text":"","notify":false,"updatestate":0,"url":""},"whitelist":{"xyz":{"name":"clientname#devicename"}}},"groups": { "0":{"name":"Group 0","lights": ["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31","32","33","34","35","36","37","38","39","40","41","42","43","44","45","46","47","48","49","50","51","52","53","54","55","56","57","58","59","60","61","62","63","64","65","66","67","68","69","70","71","72","73","74","75","76","77","78","79","80","81","82","83","84","85","86","87","88","89","90","91","92","93","94","95","96","97","98","99","100","101","102","103","104","105","106","107","108","109","110","111","112","113","114","115","116","117","118","119","120","121","122","123","124","125","126","127","128","129","130","131","132","133","134","135","136","137","138","139","140","141","142","143","144","145","146","147","148","149","150","151","152","153","154","155","156","157","158","159","160","161","162","163","164","165","166","167","168","169","170","171","172","173","174","175","176","177","178","179","180","181","182","183","184","185","186","187","188","189","190","191","192","193","194","195","196","197","198","199","200","201","202","203","204","205","206","207","208","209","210","211","212","213","214","215","216","217","218","219","220","221","222","223","224","225","226","227","228","229","230","231","232","233","234","235","236","237","238","239","240","241","242","243","244","245","246","247","248","249","250"],"type":"LightGroup","action": {"on":false,"bri":254,"hue":65280,"sat":1,"effect": "none","xy":[0.3127,0.3290],"ct":200,"alert":"none","colormode":"hs"}},"1":{"name":"Group 1","lights": [],"type":"LightGroup","action": {"on":false,"bri":254,"hue":65280,"sat":1,"effect": "none","xy":[0.3127,0.3290],"ct":200,"alert":"none","colormode":"hs"}}},"scenes" : { },"schedules" : { }} --------------------------------------------------------------------------------