1010 */
1111/*
1212 * @title Espalexa library
13- * @version 2.4.6
13+ * @version 2.5.0
1414 * @author Christian Schwinne
1515 * @license MIT
1616 * @contributors d-999
5050#include " ../network/Network.h"
5151
5252#ifdef ESPALEXA_DEBUG
53- #pragma message "Espalexa 2.4.6 debug mode"
53+ #pragma message "Espalexa 2.5.0 debug mode"
5454 #define EA_DEBUG (x ) Serial.print (x)
5555 #define EA_DEBUGLN (x ) Serial.println (x)
5656#else
6060
6161#include " EspalexaDevice.h"
6262
63+ #define DEVICE_UNIQUE_ID_LENGTH 12
6364
6465class Espalexa {
6566private:
@@ -116,17 +117,24 @@ class Espalexa {
116117 return " " ;
117118 }
118119
119- // Workaround functions courtesy of Sonoff-Tasmota
120- uint32_t encodeLightId (uint8_t idx)
120+ void encodeLightId (uint8_t idx, char * out)
121121 {
122+ // Unique id must be 12 character len
123+ // use the last 10 characters of the MAC followed by the device id in hex value
124+ // uniqueId: aabbccddeeii
125+
122126 uint8_t mac[6 ];
123127 WiFi.macAddress (mac);
124- uint32_t id = (mac[3 ] << 20 ) | (mac[4 ] << 12 ) | (mac[5 ] << 4 ) | (idx & 0xF );
125- return id;
126- }
127128
128- uint32_t decodeLightId (uint32_t id) {
129- return id & 0xF ;
129+ // shift the mac address to the left (discard first byte)
130+ for (uint8_t i = 0 ; i < 5 ; i++) {
131+ mac[i] = mac[i+1 ];
132+ }
133+ mac[5 ] = idx;
134+
135+ for (uint8_t i = 0 ; i < 6 ; i++) {
136+ sprintf (out + i*2 , " %.2x" , mac[i]);
137+ }
130138 }
131139
132140 // device JSON string: color+temperature device emulates LCT015, dimmable device LWB010, (TODO: on/off Plug 01, color temperature device LWT010, color device LST001)
@@ -136,10 +144,8 @@ class Espalexa {
136144 if (deviceId >= currentDeviceCount) {strcpy (buf," {}" ); return ;} // error
137145 EspalexaDevice* dev = devices[deviceId];
138146
139- // char buf_bri[12] = "";
140- // brightness support, add "bri" to JSON
141- // if (dev->getType() != EspalexaDeviceType::onoff)
142- // sprintf(buf_bri,",\"bri\":%u", dev->getLastValue()-1);
147+ char buf_lightid[13 ];
148+ encodeLightId (deviceId + 1 , buf_lightid);
143149
144150 char buf_col[80 ] = " " ;
145151 // color support
@@ -161,10 +167,10 @@ class Espalexa {
161167
162168 sprintf_P (buf, PSTR (" {\" state\" :{\" on\" :%s,\" bri\" :%u%s%s,\" alert\" :\" none%s\" ,\" mode\" :\" homeautomation\" ,\" reachable\" :true},"
163169 " \" type\" :\" %s\" ,\" name\" :\" %s\" ,\" modelid\" :\" %s\" ,\" manufacturername\" :\" Philips\" ,\" productname\" :\" E%u"
164- " \" ,\" uniqueid\" :\" %u \" ,\" swversion\" :\" espalexa-2.4.6 \" }" )
170+ " \" ,\" uniqueid\" :\" %s \" ,\" swversion\" :\" espalexa-2.5.0 \" }" )
165171
166172 , (dev->getValue ())?" true" :" false" , dev->getLastValue ()-1 , buf_col, buf_ct, buf_cm, typeString (dev->getType ()),
167- dev->getName ().c_str (), modelidString (dev->getType ()), static_cast <uint8_t >(dev->getType ()), encodeLightId (deviceId+ 1 ) );
173+ dev->getName ().c_str (), modelidString (dev->getType ()), static_cast <uint8_t >(dev->getType ()), buf_lightid );
168174 }
169175
170176 // Espalexa status page /espalexa
@@ -186,7 +192,7 @@ class Espalexa {
186192 }
187193 res += " \r\n Free Heap: " + (String)ESP.getFreeHeap ();
188194 res += " \r\n Uptime: " + (String)millis ();
189- res += " \r\n\r\n Espalexa library v2.4.6 by Christian Schwinne 2020" ;
195+ res += " \r\n\r\n Espalexa library v2.5.0 by Christian Schwinne 2020" ;
190196 server->send (200 , " text/plain" , res);
191197 }
192198 #endif
@@ -222,7 +228,7 @@ class Espalexa {
222228 " <URLBase>http://%s:80/</URLBase>"
223229 " <device>"
224230 " <deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>"
225- " <friendlyName>Espalexa (%s)</friendlyName>"
231+ " <friendlyName>Espalexa (%s:80 )</friendlyName>"
226232 " <manufacturer>Royal Philips Electronics</manufacturer>"
227233 " <manufacturerURL>http://www.philips.com</manufacturerURL>"
228234 " <modelDescription>Philips hue Personal Wireless Lighting</modelDescription>"
@@ -237,8 +243,8 @@ class Espalexa {
237243
238244 server->send (200 , " text/xml" , buf);
239245
240- EA_DEBUG (" Send setup.xml" );
241- // EA_DEBUGLN(setup_xml );
246+ EA_DEBUGLN (" Send setup.xml" );
247+ EA_DEBUGLN (buf );
242248 }
243249
244250 // init the server
@@ -290,15 +296,15 @@ class Espalexa {
290296 sprintf (s, " %d.%d.%d.%d" , localIP[0 ], localIP[1 ], localIP[2 ], localIP[3 ]);
291297
292298 char buf[1024 ];
293-
299+
294300 sprintf_P (buf,PSTR (" HTTP/1.1 200 OK\r\n "
295301 " EXT:\r\n "
296302 " CACHE-CONTROL: max-age=100\r\n " // SSDP_INTERVAL
297303 " LOCATION: http://%s:80/description.xml\r\n "
298304 " SERVER: FreeRTOS/6.0.5, UPnP/1.0, IpBridge/1.17.0\r\n " // _modelName, _modelNumber
299305 " hue-bridgeid: %s\r\n "
300306 " ST: urn:schemas-upnp-org:device:basic:1\r\n " // _deviceType
301- " USN: uuid:2f402f80-da50-11e1-9b23-%s::ssdp:all \r\n " // _uuid::_deviceType
307+ " USN: uuid:2f402f80-da50-11e1-9b23-%s::upnp:rootdevice \r\n " // _uuid::_deviceType
302308 " \r\n " ),s,escapedMac.c_str (),escapedMac.c_str ());
303309
304310 espalexaUdp.beginPacket (espalexaUdp.remoteIP (), espalexaUdp.remotePort ());
@@ -359,24 +365,28 @@ class Espalexa {
359365
360366 if (!udpConnected) return ;
361367 int packetSize = espalexaUdp.parsePacket ();
362- if (! packetSize) return ; // no new udp packet
368+ if (packetSize < 1 ) return ; // no new udp packet
363369
364370 EA_DEBUGLN (" Got UDP!" );
365- char packetBuffer[ 255 ]; // buffer to hold incoming udp packet
366- uint16_t len = espalexaUdp. read ( packetBuffer, 254 );
367- if (len > 0 ) {
368- packetBuffer[len ] = 0 ;
369- }
371+
372+ unsigned char packetBuffer[packetSize+ 1 ]; // buffer to hold incoming udp packet
373+ espalexaUdp. read (packetBuffer, packetSize);
374+ packetBuffer[packetSize ] = 0 ;
375+
370376 espalexaUdp.flush ();
371377 if (!discoverable) return ; // do not reply to M-SEARCH if not discoverable
372-
373- String request = packetBuffer;
374- if (request.indexOf (" M-SEARCH" ) >= 0 ) {
375- EA_DEBUGLN (request);
376- if (request.indexOf (" upnp:rootdevice" ) > 0 || request.indexOf (" asic:1" ) > 0 || request.indexOf (" ssdp:all" ) > 0 ) {
377- EA_DEBUGLN (" Responding search req..." );
378- respondToSearch ();
379- }
378+
379+ const char * request = (const char *) packetBuffer;
380+ if (strstr (request, " M-SEARCH" ) == nullptr ) return ;
381+
382+ EA_DEBUGLN (request);
383+ if (strstr (request, " ssdp:disc" ) != nullptr && // short for "ssdp:discover"
384+ (strstr (request, " upnp:rootd" ) != nullptr || // short for "upnp:rootdevice"
385+ strstr (request, " ssdp:all" ) != nullptr ||
386+ strstr (request, " asic:1" ) != nullptr )) // short for "device:basic:1"
387+ {
388+ EA_DEBUGLN (" Responding search req..." );
389+ respondToSearch ();
380390 }
381391 }
382392
@@ -452,13 +462,12 @@ class Espalexa {
452462 return true ;
453463 }
454464
455- if (req.indexOf (" state" ) > 0 ) // client wants to control light
465+ if (( req.indexOf (" state" ) > 0 ) && (body. length () > 0 ) ) // client wants to control light
456466 {
457467 server->send (200 , " application/json" , F (" [{\" success\" :{\" /lights/1/state/\" : true}}]" ));
458468
459469 uint32_t devId = req.substring (req.indexOf (" lights" )+7 ).toInt ();
460470 EA_DEBUG (" ls" ); EA_DEBUGLN (devId);
461- devId = decodeLightId (devId);
462471 EA_DEBUGLN (devId);
463472 devId--; // zero-based for devices array
464473 if (devId >= currentDeviceCount) return true ; // return if invalid ID
@@ -530,7 +539,7 @@ class Espalexa {
530539 String jsonTemp = " {" ;
531540 for (int i = 0 ; i<currentDeviceCount; i++)
532541 {
533- jsonTemp += " \" " + String (encodeLightId ( i+1 ) ) + " \" :" ;
542+ jsonTemp += " \" " + String (i+1 ) + " \" :" ;
534543 char buf[512 ];
535544 deviceJsonString (i+1 , buf);
536545 jsonTemp += buf;
@@ -540,7 +549,6 @@ class Espalexa {
540549 server->send (200 , " application/json" , jsonTemp);
541550 } else // client wants one light (devId)
542551 {
543- devId = decodeLightId (devId);
544552 EA_DEBUGLN (devId);
545553 if (devId > currentDeviceCount)
546554 {
0 commit comments