@sh20 That’s not actually “documentation” per se, but it is a reference implementation that accesses the same APIs that the device apps (i.e., their Android app) uses to communicate with Dyson and the devices on your account.
I started creating a “(Connect)” labs app that would work like many of the other web service device handlers. The current roadblock is a combination of three things that together make this integration impossible:
- Dyson’s API uses a self-signed certificate, issued by a self-signed Dyson CA (certificate authority)
- Dyson’s API only listens on the HTTPS port (443), and does not accept requests on the insecure HTTP (80) port.
- SmartThings has no way to support self-signed certificates.
Elaborated info:
Dyson has chosen to use a self-signed certificate for their SSL encryption. That means they aren’t using a publicly known CA (certificate authority) to issue a certificate for their https web server, but instead they have their own internal CA certificate, and have issued a certificate for the server using that CA.
The device apps themselves are able to use certificate pinning to communicate with that server, by holding the “expected” certificate inside the app, and then ensuring that the api’s certificate matches the certificate that it expects. So the application can still verify that you aren’t victim to a MITM attack, even though it’s not from a CA that is known by the device (since it is at least known by the app). But that means that your web browser (and all other HTTP request libraries) will not be able to verify that the SSL certificate is valid, because it’s not a public, well-known, and trusted CA issuer.
In both the Python and nodejs implementations, they have gotten around that by explicitly instructing the HTTP libraries to ignore SSL errors. What that means is when the library makes the request, it sees the Dyson certificate, which was issued by the Dyson self-signed CA certificate holder. When it tries to make a request to that host, it will fail because it cannot validate the Dyson CA. By instructing those libraries to ignore errors (rejectUnauthorized: false
in nodejs, and Verify: False
in Python), it can continue making the API calls because it is not validating the SSL certificate. That leaves those implementations open to a MITM attack, since it cannot verify that the host it’s communicating with is in fact Dyson.
The crux of the issue is that the SmartThings HTTP library has no mechanisms in place for either a) ignoring SSL errors, such as how the nodejs and Python libraries do, or b) supplying a known certificate for the API service itself, or the issuing CA. Because of that, the default behavior is to totally abort the API call, because of the unknown certificate authority.
I understand SmartThings’s stance to reject unverified SSL requests, as that is the most secure way to do this. The other option that would allow this to work, without sacrificing security, would be to allow us to supply the expected certificate for either the Dyson API server, or the Dyson self-signed CA, so that the hub would be able to verify that it is in fact communicating with Dyson and not a fake MITM spy. Theoretically, they could allow that by either letting the device owner upload and install certificates that will add to the trusted certificate chain that can be used to verify any matching HTTP requests from any smartapp; or allowing the developer to supply the known certificate in a “sandboxed” way so that the certificate override is only used within the smartapp itself. Either of those options would allow making these API calls without SmartThings rejecting it, and maintains the security of preventing MITM attacks.
Another option would be to run your own HTTP proxy on your local network, and have the smartapp hit that proxy instead. That proxy could be exposed over HTTP (insecure), and then make its own API calls to the HTTPS (secured with self-signed cert) API, using the Python or NodeJS library; or simply proxy the HTTP calls straight to the HTTPS server, ignoring the certificate errors.
One last-resort option would be to communicate directly with the fan itself, completely bypassing the Dyson API. Based on information I was able to find, it looks like the app communicates locally with the fan using the MQTT protocol (a realtime protocol, similar to XMPP, like how instant messaging services operate). That has its own problems, because there’s no way for the SmartThings hub to “speak” the MQTT protocol. I have not explored whether the fan can respond to HTTP requests or not, as if it can support that, then you can bypass both the Dyson API and the Fan’s MQTT service, and just speak directly to the fan over HTTP.
Then yet another last-last-resort option would be to bypass the Dyson API server, and instead scrape the HTML frontend of the dyson website. The problem with this option is that the website does not have a way to monitor or control the machines – it only shows you which machines you have registered on your account.