-
Notifications
You must be signed in to change notification settings - Fork 5
WebSockets API
This portion of the API was discovered using traffic inspection techniques between the app an the hub.
This approach was taken as the Serial Protocol only allows a single TCP connection at once. An incoming connection disconnects an existing connection, where as the WebSockets API allows several connections at once.
Please note that blind, roller and shade are used interchangeably - they mean the same thing in this project.
The connection by the App to the hub is via a secure web sockets connection (wss), on the standard port 443.
The protocol is JSON based, and is pure polling (ie: no events appear to be fired by the hub, it's all in response to queries from the app).
All examples below have been run through js-beautify. The app implementation uses a single line / minimised JSON.
Some value have been obfuscated, and replaced with xxx as appropriate.
This is fired every 3 seconds or so by the app. It's a request for the current status of all of the rollers:
{
"method": "shadow",
"src": "app",
"id": 1598162592
}
| Field | Description |
|---|---|
| method | Value is always shadow
|
| src | The source of the query, in this implementation, it always uses app
|
| id | Some sort of unique ID. The app appears to use the int of time.time() (UNIX Epoch), so this is replicated in this library in the heartbeat method. |
The response is as follows. This contains the data that's used by this library.
{
"id": 1598162592,
"src": "esp32_XXXXXX",
"dst": "app",
"result": {
"reported": {
"hubId": "100XXXX",
"name": "default",
"mac": "c4:4f:33:XX:XX:XX",
"onlineLatest": 1598162603.550303,
"mfi": {
"PD": "XXXXXXXXXXXXXXXX",
"manufacturer": "Rollease Acmeda",
"model": "MT02-0401-067001"
},
"firmware": {
"version": "1.0.0",
"RFversion": "B10"
},
"shades": {
"VEB": {
"is": true,
"ol": true,
"mp": 100,
"vo": "11.2D24",
"ls": 1,
"rs": -76
},
"S1S": {
"is": true,
"ol": true,
"mp": 0,
"ta": 0,
"vo": "11.3D24",
"ls": 1,
"rs": -77
},
"D55": {
"is": true,
"ol": true,
"mp": 30,
"vo": "11.4D24",
"ls": 1,
"rs": -70
},
"PF8": {
"is": true,
"ol": true,
"mp": 100,
"vo": "12.4D24",
"ls": 1,
"rs": -83
},
"0R5": {
"is": true,
"ol": true,
"mp": 0,
"ta": 0,
"vo": "11.3D24",
"ls": 1,
"rs": -101
},
"YQX": {
"is": true,
"ol": true,
"mp": 100,
"vo": "11.3D24",
"ls": 1,
"rs": -69
},
"35W": {
"is": true,
"ol": true,
"mp": 0,
"ta": 0,
"vo": "11.4D24",
"ls": 1,
"rs": -73
},
"Q46": {
"is": true,
"ol": true,
"mp": 100,
"vo": "11.2D24",
"ls": 1,
"rs": -72
}
}
}
}
}
| Field | Description |
|---|---|
| id | The same id as used in the query. This library ignores this value |
| src | Appears to be the type of processor (ESP32) and the last 3 bytes of the MAC address |
| dst | Appears to be the the same as the src in the query |
| result | The response to the query, data analysed so far always gives this a single key value of reported
|
| reported | The actual data/payload |
| hubId | The serial number as written on the bottom of the hub |
| name | The name of the hub as configured in the App |
| mac | The MAC address of the hub, I have not tested if the wired Ethernet has the same MAC as the WiFi, nor what is reported if they are different |
| onlineLatest | Assumed to be last time the hub was able to connect to the cloud servers (not verified), in UNIX Epoch |
| mfi > pd | Unsure, have not investigated |
| mfi > manufacturer | The manufacturer of the hub |
| mfi > model | The model of the hub |
| firmware > version | The currently running firmware version |
| firmware > RFversion | Assumed to be the firmware of the current RF subsystem, this may run a different processor for the 433MHz communications |
| shades | An object with all of the actual shades/rollers, the key is the ID of the shade ("Device Address" in the app), and the value is the current status, see the following table for more information |
The shades key in the response output has the data that's of most value to the app. The key in the shades object is the ID of the shade, and the fields for the value of the object are as follows:
| Field | Description |
| is | boolean - is the blind stationary, when moving this is false |
| ol | boolean - is the blind online and communicating with the hub. Note this only appears to update ever 15-30 minutes |
| vo | string - the battery voltage, type and version of the devices. The voltage is a float before the first letter, the letter is the type (see the Serial Protocol for details) and the final 2 digits are the version. Note this field is not always present, especially when the hub has just been rebooted. See the Query request below for further details |
| ls | int - if the limits have been set for the shade, this is unverified. The only value I have seen here is 1 - and all my shades had their limits set |
| rs | int - signal strength of the 433MHZ connection to the hub |
You will note the name of the roller is never sent via this websocket connection. The app appears to get it by making a query to the cloud services. This library works around this by making a short lived connection to the Serial Protocol TCP port.
This is fired when further details are required from the roller. This library only uses this when the vo field is missing on a response. Only a limited number of these requests can be made per second. If too many requests are made too quickly, they will be ignored. To emulate the app, a query with more than one shades value is not used.
{
"method": "shadow",
"args": {
"desired": {
"shades": {
"S1S": {
"query": true
}
}
},
"timeStamp": 1598162567.133
}
}
Most fields are as above, with the exception of the following:
| Field | Description |
|---|---|
| args > desired > shades | The key is the ID of the desired shade to get further details from ("Device Address" in the app) |
| args > desired > shades > <ID> > query | Set to true to request further details |
| args > timeStamp | The current time as a float in UNIX Epoch |
This request does not receive a reply, rather another query request is used to see the current status.
This command actually moves the shade to the desired position.
{
"method": "shadow",
"args": {
"desired": {
"shades": {
"S1S": {
"movePercent": 0
}
}
},
"timeStamp": 1598162580.458
}
}
Fields of note not already outlined above are:
| Field | Description |
|---|---|
| args > desired > shades > <ID> > movePercent |
int - The position to move the blind to, between 0 and 100, where 0 is fully open, and 100 is fully closed |
This command does not give any response, the current status needs to be queried using the query command.