Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Anyone tried to use ESP8266HueEmulator with Amazon Alexa? #62

Open
targence opened this issue Jan 21, 2017 · 38 comments
Open

Anyone tried to use ESP8266HueEmulator with Amazon Alexa? #62

targence opened this issue Jan 21, 2017 · 38 comments

Comments

@targence
Copy link

I tried to use ESP8266HueEmulator with Amazon Alexa, but Alexa can't find new device.
She said: "If you have Philips Hue, press the button on Hue bridge"...

@probonopd
Copy link
Owner

Sorry, I don't have an Alexa device.

@bcatalin
Copy link

I've tried, but I have the same problem with the button. First I need to see why the current code is not working fine with hue mobile app and then I will look to Alexa.

Now is connecting to mobile app but disconnect are reconnect continue and I can see only white color no matter what I select.

@jdschuitemaker
Copy link

I am seeing same behavior as @targence and @bcatalin are reporting, but I am using a ReSpeaker (Seeedstudio kickstarter) and not an official Amazon device.

  • The message from Alexa is a different. She is just reporting "no devices found".
  • The Hue app on Android seems to loose it's connection with the Hue Bridge or needs a long time to reconnect.

In addition, but not sure if that really is a problem, after registering and logging in on the site meethue.com you can add Hue Bridges. Should this only work for the real Hue's?

@probonopd
Copy link
Owner

Sorry, but registering and logging in on the site meethue.com is not implemented at all for ESP8266HueEmulator. Not sure if Philips even would allow this for 3rd party devices.

@Quanghoster
Copy link
Contributor

Quanghoster commented Sep 25, 2017

I've been trying to debug this with a packet sniffer and not cracked it yet. I have the hueUpnp python script running on my laptop and this works perfectly. Strange thing I'm seeing is that the ESP doesn't always seem to see the SSDP broadcast from the echo and when it does and the serial log says it's sending a response, I don't see the response on the network. I'm beginning to think it could be more performance related than code. Has anyone else made any progress?

@dtila
Copy link
Contributor

dtila commented Sep 29, 2017

Google Home doesn't find either. I think it's a problem in the discovery since I have tried other hue emulators and they are find by Google.

@Quanghoster
Copy link
Contributor

I've tried two I've found for esp and neither are found by the echo. Not sure I can categorically state it's the esp, but possibly. Ive used wireshark to look at the discovery interaction and it seems that the esp doesn't respond to the echo broadcast. Will have another look this evening

@Quanghoster
Copy link
Contributor

Made some progress... The echo sends a search looking for devices with a name ending "basic:1" the code seems to be case sensitive looking for "Basic:1" as when I changed to match the echo search the esp responded. Still not registering because a put all lights command hasn't yet been implemented in code, which seems to be the next step. I will look in to that next. So some progress

@dtila
Copy link
Contributor

dtila commented Oct 2, 2017

I've had a look also, but without luck since now. Here is a simple example of Hue Emulation (simple anc clear) which works with Alexa and Google Home also https://github.com/home-assistant/home-assistant/blob/dev/homeassistant/components/emulated_hue/upnp.py

I have tried to replace the device type and the reply is quite similar with what's there. Maybe you have more luck :)

@dtila
Copy link
Contributor

dtila commented Oct 2, 2017

A thing which I have just notice, is that the broadcasted UDN by the ESP code is not a proper guid:

  <UDN>uuid:2f402f80-da50-11e1-9b23-5C:CF:7F:9F:26:6B</UDN>

I will try to fix this and keep you posted !

@mariusmotea
Copy link
Contributor

Check this issue where you have lot of upnp traffic dumps from original hue bridge.
Here are my ssdp functions:

def ssdpSearch():
    SSDP_ADDR = '239.255.255.250'
    SSDP_PORT = 1900
    MSEARCH_Interval = 2
    multicast_group_c = SSDP_ADDR
    multicast_group_s = (SSDP_ADDR, SSDP_PORT)
    server_address = ('', SSDP_PORT)
    Response_message = 'HTTP/1.1 200 OK\r\nHOST: 239.255.255.250:1900\r\nEXT:\r\nCACHE-CONTROL: max-age=100\r\nLOCATION: http://' + getIpAddress() + ':80/description.xml\r\nSERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.20.0\r\nhue-bridgeid: ' + (mac[:6] + 'FFFE' + mac[6:]).upper() + '\r\n'
    custom_response_message = {0: {"st": "upnp:rootdevice", "usn": "uuid:2f402f80-da50-11e1-9b23-" + mac + "::upnp:rootdevice"}, 1: {"st": "uuid:2f402f80-da50-11e1-9b23-" + mac, "usn": "uuid:2f402f80-da50-11e1-9b23-" + mac}, 2: {"st": "urn:schemas-upnp-org:device:basic:1", "usn": "uuid:2f402f80-da50-11e1-9b23-" + mac}}
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.bind(server_address)

    group = socket.inet_aton(multicast_group_c)
    mreq = struct.pack('4sL', group, socket.INADDR_ANY)
    sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)

    print("starting ssdp...")

    while run_service:
              data, address = sock.recvfrom(1024)
              if data[0:19]== 'M-SEARCH * HTTP/1.1':
                   if data.find("ssdp:all") != -1:
                       sleep(random.randrange(0, 3))
                       print("Sending M Search response")
                       for x in xrange(3):
                          sock.sendto(Response_message + "ST: " + custom_response_message[x]["st"] + "\r\nUSN: " + custom_response_message[x]["usn"] + "\r\n\r\n", address)
                          print(Response_message + "ST: " + custom_response_message[x]["st"] + "\r\nUSN: " + custom_response_message[x]["usn"] + "\r\n\r\n")
              sleep(1)

and for broadcast:

def ssdpBroadcast():
    print("start ssdp broadcast")
    SSDP_ADDR = '239.255.255.250'
    SSDP_PORT = 1900
    MSEARCH_Interval = 2
    multicast_group_s = (SSDP_ADDR, SSDP_PORT)
    message = 'NOTIFY * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nCACHE-CONTROL: max-age=100\r\nLOCATION: http://' + getIpAddress() + ':80/description.xml\r\nSERVER: Linux/3.14.0 UPnP/1.0 IpBridge/1.20.0\r\nNTS: ssdp:alive\r\nhue-bridgeid: ' + (mac[:6] + 'FFFE' + mac[6:]).upper() + '\r\n'
    custom_message = {0: {"nt": "upnp:rootdevice", "usn": "uuid:2f402f80-da50-11e1-9b23-" + mac + "::upnp:rootdevice"}, 1: {"nt": "uuid:2f402f80-da50-11e1-9b23-" + mac, "usn": "uuid:2f402f80-da50-11e1-9b23-" + mac}, 2: {"nt": "urn:schemas-upnp-org:device:basic:1", "usn": "uuid:2f402f80-da50-11e1-9b23-" + mac}}
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.settimeout(MSEARCH_Interval+0.5)
    ttl = struct.pack('b', 1)
    sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl)
    while True:
        for x in xrange(3):
            sent = sock.sendto(message + "NT: " + custom_message[x]["nt"] + "\r\nUSN: " + custom_message[x]["usn"] + "\r\n\r\n",multicast_group_s)
            sent = sock.sendto(message + "NT: " + custom_message[x]["nt"] + "\r\nUSN: " + custom_message[x]["usn"] + "\r\n\r\n",multicast_group_s)
            #print (message + "NT: " + custom_message[x]["nt"] + "\r\nUSN: " + custom_message[x]["usn"] + "\r\n\r\n")
        sleep(60)

both are in python but you can understand very easy what they are sending. These are made to be identical with original hue bridge (timing is also the same).

@Quanghoster
Copy link
Contributor

I will look into this further over the next few says. I need to set up my raspberry pi 3 as an access point and get it to send all traffic to wireshark first. I'm not seeing everything at the moment. I have downloaded that python emulator and see that works so just need to be able to compare both in full

@Quanghoster
Copy link
Contributor

LightService_cpp.txt
Ok, I've made some progress with this . A couple of changes I've made are as follows.

in LightService.cpp
For the method
void addLightJson(aJsonObject* root, int numberOfTheLight, LightHandler *lightHandler)

add the following:
aJson.addStringToObject(light, "manufacturername", "somevalue"); // type of lamp (all "Extended colour light" for now)
aJson.addStringToObject(light, "swversion", "0.1"); // type of lamp (all "Extended colour light" for now)

I also changed the following in void sendJson()
from
HTTP->send(200, "text/plain", msgStr);
to
HTTP->send(200, "application/json", msgStr);

I did add a couple of functions that changed the format of the json response for /api/xxxxx/lights calls, to be closer to what I've observed with the echo, but they are no longer referenced. I've left them in the attached in case they are needed at some point. This seems to have solved the discovery issues with echo for me so I'll spend a bit of time looking at the control elements...

@probonopd
Copy link
Owner

probonopd commented Oct 3, 2017

Thanks @Quanghoster 👍 Do you want to send a pull request or do you want me to merge the above?

@dtila
Copy link
Contributor

dtila commented Oct 3, 2017

I can confirm that this works with Google Home also, Thanks for the tip!

From what I have seen, the line

//SSDP.setMessageFormatCallback(ssdpMsgFormatCallback);

has been commented. The message transmission is handled anyway by the SSDP library. However, we may consider in the near future to loop thought 3 item arrays to change the NT and USN like @mariusmotea mentioned in the previous post.

custom_message = {0: {"nt": "upnp:rootdevice", "usn": "uuid:2f402f80-da50-11e1-9b23-" + mac + "::upnp:rootdevice"}, 1: {"nt": "uuid:2f402f80-da50-11e1-9b23-" + mac, "usn": "uuid:2f402f80-da50-11e1-9b23-" + mac}, 2: {"nt": "urn:schemas-upnp-org:device:basic:1", "usn": "uuid:2f402f80-da50-11e1-9b23-" + mac}}

For now it works, and I will create a push request with more additional stuff which I have noticed

@Quanghoster
Copy link
Contributor

Cool. I have been working on testing turning a strip on and off whist monitoring with wireshark. The state is changing correctly but the echo states something went wrong. The only difference between this and a working emulator is that the http connection header is set to keep-alive on the working one and closed on this. Still needs mire investigation...

@probonopd
Copy link
Owner

Happy to merge one pull request per issue :-)

@Quanghoster
Copy link
Contributor

Sure. Go ahead what we have so far and I will investigate further over the next few days. :)

@Quanghoster
Copy link
Contributor

WireShark is so useful with a Raspberry P set up as a WAP and a sniffer :-)
Ok,I now have Alexa responding correctly when changing state of a Led Strip by voice command. There was an issue with the json that was being sent in lightsIdFn().

I've changed this function to call a new function addSingleLightJson instead of addLightJson

void addSingleLightJson(aJsonObject* root, int numberOfTheLight, LightHandler *lightHandler);
void lightsIdFn(WcFnRequestHandler *whandler, String requestUri, HTTPMethod method) {
int numberOfTheLight = atoi(whandler->getWildCard(1).c_str()) - 1;
LightHandler *handler = LightService.getLightHandler(numberOfTheLight);
switch (method) {
case HTTP_GET: {
aJsonObject *root;
root = aJson.createObject();
addSingleLightJson(root, numberOfTheLight, handler);
sendJson(root);
break;
}

The new function is as follows...

void addSingleLightJson(aJsonObject* light, int numberOfTheLight, LightHandler *lightHandler) {
if (!lightHandler) return;
String lightName = "" + (String) (numberOfTheLight + 1);

aJson.addStringToObject(light, "manufacturername", "OpenSource"); // type of lamp (all "Extended colour light" for now)
aJson.addStringToObject(light, "modelid", "LST001"); // the model number
aJson.addStringToObject(light, "name", ("Hue LightStrips " + (String) (numberOfTheLight + 1)).c_str()); // // the name as set through the web UI or app
aJsonObject state;
aJson.addItemToObject(light, "state", state = aJson.createObject());
HueLightInfo info = lightHandler->getInfo(numberOfTheLight);
aJson.addBooleanToObject(state, "on", info.on);
aJson.addNumberToObject(state, "hue", info.hue); // hs mode: the hue (expressed in ~deg
182.04)
aJson.addNumberToObject(state, "bri", info.brightness); // brightness between 0-254 (NB 0 is not off!)
aJson.addNumberToObject(state, "sat", info.saturation); // hs mode: saturation between 0-254
double numbers[2] = {0.0, 0.0};
aJson.addItemToObject(state, "xy", aJson.createFloatArray(numbers, 2)); // xy mode: CIE 1931 color co-ordinates
aJson.addNumberToObject(state, "ct", 500); // ct mode: color temp (expressed in mireds range 154-500)
aJson.addStringToObject(state, "alert", "none"); // 'select' flash the lamp once, 'lselect' repeat flash for 30s
aJson.addStringToObject(state, "effect", info.effect == EFFECT_COLORLOOP ? "colorloop" : "none");
aJson.addStringToObject(state, "colormode", "hs"); // the current color mode
aJson.addBooleanToObject(state, "reachable", true); // lamp can be seen by the hub aJson.addStringToObject(root, "type", "Extended color light"); // type of lamp (all "Extended colour light" for now)

aJson.addStringToObject(light, "swversion", "0.1"); // type of lamp (all "Extended colour light" for now)
aJson.addStringToObject(light, "type", "Extended color light"); // type of lamp (all "Extended colour light" for now)
aJson.addStringToObject(light, "uniqueid", ((String) (numberOfTheLight + 1)).c_str());

}

Here is the updated cpp. I must figure out how to deal with puah/pull, lol
LightService_cpp.txt

@Quanghoster
Copy link
Contributor

Now have the echo turning on an off the EP-12F built in LED by command. Mine is on pin 2 rather than LED_BUILTIN, but works a treat. I've been making some changes to use the WS2812FX library rather than the NeoPixelBus as I find it a lot easier to work with

@probonopd
Copy link
Owner

Just click the "Edit" button on the GitHub page for that file, make your changes, click the button at the bottom of the page. When asked, create a Pull Request and send it. Should not take longer than a minute to do.

This way, your work will receive the proper attribution in the project history.
Thank you.

@Quanghoster
Copy link
Contributor

Ok, Think I've done that now using GitHub Desktop

@probonopd
Copy link
Owner

So far I don't see anything - please do a "git commit" and a "git push" in GitHub Desktop.

@Quanghoster
Copy link
Contributor

permissions?

@fighterxxl
Copy link

Wow guys this is just great! Making work this projekt with echo would be awsome.
I own a echo dot for a few days and it was fun to make the smart home Integration with the fauxmo lib for my 433mhz power outlets.
Im looking forward to do a simalar thing my colored led strip. I think this project would be a better choice than trying to write a custom alexa skill.
Can you please tell me if there is something special for me to know about the echo discovery? Or will this work out of the box with your enhancement?
I am really looking forward to try this :-)

cheers
FighterXXL

@Quanghoster
Copy link
Contributor

Hi @fighterxxl . We have some changes we are checking in at the moment that sort out discovery with Alexa. I have an ESP-12E here that Alexa sees and can turn on and off the onboard LED, so yes it's pretty well good to go with these changes. Thanks to @probonopd for a great project

@Quanghoster
Copy link
Contributor

finally have some working code using NeoPixelBus. I've derived a new class from Lighthandler called Echohandler which can be used in place of Pixelhandler. it allows a neopixel strip to be turned on with some very basic animation and turned off with the echo. need contribute access to load the enhancements... :)

@fighterxxl
Copy link

Hi @Quanghoster, I had a bit trouble yesteday building this Project.
Using your last post LigthService.cpp I wasn't able to use the ESP8266 with alexa. But the alexa device discovery triggerd some json on the serial :)
I am curious about your EchoHandler and I like to test that. If I am too stupid to use it with alexa, could you provide a few instructionlines how to use it with alexa?
And of cource thanks @probonopd for this project :)

cheers
FighterXXL

@fighterxxl
Copy link

Hi *,
got it to work. But its too early for me to use. Alexa don't know color commands in my country. Simple on and off works but colors won't -.-
Too bad, maybe in a few month...

cheers
FighterXXL

@Quanghoster
Copy link
Contributor

Quanghoster commented Oct 6, 2017 via email

@probonopd
Copy link
Owner

@Quanghoster and @fighterxxl thanks for the progress you are making. Please do send GitHub pull requests. You don't need special permissions for this.

https://help.github.com/articles/creating-a-pull-request/

@Quanghoster
Copy link
Contributor

I've figured out how to do one now I've forked your project. Lol. I hope to look in to Alexa color changes this week. I can turn off and on and dim but pretty sure you can change colours now. But not with what we have here atm

@Quanghoster
Copy link
Contributor

OK, I've been busy investigating colour change support for alexa and, as I understand it, we will need a different approach. it looks like alexa doesn't do this via the local rest api on the hub. it does it via the smart home skill. So. to be able to do this, and possibly set scenes, we need to use a cloud service as skills can't access the hub. I have created an initial alexa smart home skill to test with and am looking at interfacing with aws iot. From there I can build in mtqq connectibity to this project to support the various calls. aws-iot seems a natural selection for this because you need an oauth provider to Link accounts to a smart home skill. it's more natural to use a smart home skill than a custom skill as you don't need an activation word. I'll post more on this in a few days

@Quanghoster
Copy link
Contributor

I've just submitted a pull request which includes a fix for Alexa discovery.

I have also made some progress with getting a test sketch working to connect to to AWS IoT with an MQTT client to subscript to shadow update messages along with an initial node.js alexa smarthome skill which will eventually connect to IoT aswell. A little early to post anything just yet, but it's now coming along nicely. Hopefully some further updates in a week or so

@Quanghoster
Copy link
Contributor

I'm getting some wierdness happening since I resynced my repository.
Discovery is working with the echo but there seems to be a problem with the /state/ command, both with the echo and an android Hue app.
Debugging the parsing with the webserver shows the following.
It looks like the args are not being processed correctly and I end up with HTTP->arg("plain").c_str() being empty.
Is anyone else seeing this problem?

Andy.

method: PUT url: /api/null/lights/1/state search:
headerName: Content-Type
headerValue: application/x-www-form-urlencoded
headerName: User-Agent
headerValue: Dalvik/2.1.0 (Linux; U; Android 6.0.1; D6503 Build/23.5.A.1.291)
headerName: Host
headerValue: 10.10.100.17
headerName: Connection
headerValue: Keep-Alive
headerName: Accept-Encoding
headerValue: gzip
headerName: Content-Length
headerValue: 20
args: {"on":true,"bri":90}
args count: 1
pos 0=@ -1 &@ -1
arg missing value: 0
args count: 0
Plain: {"on":true,"bri":90}
Request: /api/null/lights/1/state
Arguments: {"on":true,"bri":90}
44182
[{"error":{"type":2,"address":"/api/null/lights/1/state","description":"Bad JSON body in request"}}]

@Quanghoster
Copy link
Contributor

Strange,
It seems the echo is sending as content-type "x-www-form-urlencoded" which seems to be related to the issue. I'm not sure that make sense, but it works for others.

The following works ok from another 3rd party Windows 10 app.

New client
method: PUT url: /api//lights/1/state search:
headerName: Content-Length
headerValue: 19
headerName: Content-Type
headerValue: application/json; charset=utf-8
headerName: Host
headerValue: 192.168.1.22
headerName: Connection
headerValue: Keep-Alive
args:
Plain: {"bri":1,"on":true}
Request: /api//lights/1/state
Arguments:
arg0plain
325550
[{"success":{"/lights/1/state/bri":1}},{"success":{"/lights/1/state/on":true}}]

@Quanghoster
Copy link
Contributor

Quanghoster commented Oct 22, 2017

Ok, I've figured this out. I'm not sure why the echo is posting with Content-Type of application/x-www-form-urlencoded, but the webserver doesn't behave as expected with this and therefore doesn't fill the plain variable.
I've hacked Parsing.cpp at line 166 to force isEncoded to false to avoid the problem for now. This game me a successful response to /lights/n.

  if (headerName.equalsIgnoreCase("Content-Type")){
    if (headerValue.startsWith("text/plain")){
      isForm = false;
    } else if (headerValue.startsWith("application/x-www-form-urlencoded")){
      isForm = false;
      isEncoded = false;// <--- HACK changed from true
    } else if (headerValue.startsWith("multipart/")){
      boundaryStr = headerValue.substring(headerValue.indexOf('=')+1);
      boundaryStr.replace("\"","");
	  isForm = true;
    }
  } else if (headerName.equalsIgnoreCase("Content-Length")){
    contentLength = headerValue.toInt();
  } else if (headerName.equalsIgnoreCase("Host")){
    _hostHeader = headerValue;
  }
}

This then showed up the json response format for /lights/n/state which I'll shortly post a PR for.

Andy.

@MopheusDG
Copy link

Actually I used this with Alexa. As soon as the ESP8266 booted, did a search in Alexa and it found the 2 strips without issues. I think it could be more useful if the library could provide an easier way to define Lamps names, cause I have to edit the other files to do it. Maybe we should build something more like the FauxmoESP library where you define each "device" easily, but it's just an idea of course... I still need to take a deeper look into this code and so far, works fine except when you want to create a Room setup, but Alexa doesn't need that, it just sees the lamps. At least on my Echo Dot v1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants