Coding Stephan

Local API for appliances

I’m a home automation enthusiast, so that’s out. I’ve been using various systems for years, I even build some home automation packages, like sonos2mqtt and ipcam2mqtt. When buying devices I always look for devices that have a local API, so I can integrate them in my home automation system. Devices that require the cloud to be controllable are getting a points deduction when comparing devices.

In my opinion all devices should have a local api, and if they have a cloud api, it should be optional. I don’t want to be forced to use the cloud and I want to be able to control my devices locally.

I discovered that my oven talks to questionable countries so I disconnected it from the internet. Recently Haier threatened an open-source developer with legal action because he made a home assistant plugin for his own Haier applicance.

Dishwasher on a cloud

Haier issue

Haier complains that the developer is “causing economical damage” to Haier. What they actually mean is, your app seems to be polling our api to often. And I’ve to agree with that, polling every 5 seconds seems a bit over the top. While this is roughly the same as the app does, the user won’t be using the app all the time. So I can understand that Haier doesn’t want to be flooded with requests. But that’s not the real issue here.

The real issue is that Haier decided it wants to control all the data, and send it through their own servers even if the app and the appliance are on the same wifi network. If you have this appliance in your home and you bought it to be a smart device. There is absolutely no reason why all the communication between the app and the appliance should go through the internet. The cloud must be completely optional to support users who want to control their devices from outside their home. But it should never be mandatory.

Why a local API

As you might have guessed, I’ll be talking about local control of devices you own. And why this should be a number one priority for all users (and thus also for manufacturers).

Pros of Local API

There are a lot of reasons why a local API is better than a cloud API. Here are some of them:

  • Will continue to work even if the manufacturer goes bankrupt.
  • Faster app response times, no need for a roundtrip to the cloud.
  • No need for an internet connection to control your devices.
  • “Unlimited” api calls.
  • No additional costs for the manufacturer to maintain the cloud infrastructure.
  • You might get a “free” developed app, if you open up the api and your own app is very well build.

Cons of a local API

It’s not all sunshine and rainbows, there are some downsides to a local API:

  • The vendor has less control over what the device does.
  • The vendor can’t collect data about the usage of the device (if the user blocks internet access from it).
  • Security seems harder to implement, since it has to be done on the device itself.

Build a local API

When controlling a local api for your device, there are a few things to keep in mind. Here are just a few pointers to get you started. Feel free to use/change these to your own liking.

API

If you decided that the device should have a local api, I suggest at least the following endpoints:

  • GET /api/v1/info - Returns information about the device, like the model, serial number, firmware version, etc.
  • POST /api/v1/info - Allow updating the information about the device, like the name, location, etc.
  • GET /api/v1/state - Returns the current state of the device, like the current temperature, the current mode, etc.
  • POST /api/v1/state - Sets the state of the device, like the temperature, the mode, etc.

And to allow other developers to control the device, you should also add an open api endpoint for the device, and maybe even a swagger ui. Where developers can just try out the api.

{
  "name": "My device",
  "location": "Kitchen",
  "model": "Dishwasher 3000",
  "serialNumber": "1234567890",
  "logoUrl": "https://example.com/dishwasher-3000.png",
  "supportUrl": "https://example.com/support/dishwasher-3000",
}
{
  "expectedFinishTime": "2024-01-21T09:57:06.331Z",
  "currentCycle": "prewash",
  "doorOpen": false,
  "currentTemperature": 20.5,
  "targetTemperature": 50,
  //...
}

Status updates

Consistently polling the device for status updates is not the best use of resources. Instead the device could implement a websocket server, if you connect to this endpoint the device can send out updates when the state changes. This way the local integration can be up-to-date all the time. The websocket could send messages like:

{
  "id": 638414352600000000,
  "type": "state",
  "data": {
    "temperature": 20.5,
    "mode": "heating"
  }
}

where the type is the name of the endpoint where this data could normally be fetched from (e.g. state or info) and the data is the data has the same structure as the data returned from the endpoint.

Security

Start the device with the local api turned off, if the user wants to connect the device to the wifi network, you show them a message with the question do you want to enable cloud access as well. During the initial setup your app should exchange the api key it needs to use to control the device on the local network. Discovery can work through mDNS or SSDP, but entering the IP address and API key should also be possible. Then ask the user again if they want to use the cloud to control the appliance from outside their home and have them create an account.

It should be possible to disable the cloud access at any time, and it should also be possible to “reset” the api key. This way the user can revoke access to the device from the app.

Instead of using a static api key, you could also allow the user to set an openid connect configuration url, and have the appliance trust all the tokens generated by that openid connect provider. This way you could get 100% security, but this is really only for the advanced users.

If you don’t want the communication to be intercepted, you could use https with a self-signed certificate and have that certificate pinned in the app. Public trusted certificates cannot be used since the device does not have a public domain name and just an local ip. Al though you could make the device fetch a certificate that looks like device-serial.localdevices.somedomain.com, have that certificate signed by a public CA and then emit that domain name through mDNS. But this is a lot of work, and I don’t think it’s worth the effort.

Conclusion

I hope that more manufacturers will start to implement local api’s for their devices. This way we can keep using our devices even if the manufacturer goes bankrupt, or decides to stop supporting the device. And we can keep using our devices even if the internet is down.

And one advice to Haier, implement a local api and stop threatening developers who want to control their own devices. You’ll get a lot more happy customers if you do. Your app will respond faster and your cloud costs will be cut in half (at least). Lets face it, most users will be controlling their devices from home anyway, and those tech savvy people using home assistant will not ever connect to your cloud again because the local api is so much faster.

Having a local api is a win-win for everyone, so please implement it. It would definitely get you off the I would never buy from this manufacturer again list!

If you’re working for a manufacturer and you want to implement a local api, but you don’t know where to start. Feel free to contact me and I’ll be happy to help you out.