Real Time Events
Besides HTTP commands to control devices, a WebSocket connection can be established, via OAuth account credentials, so a real time events about device status changes can be received, some simple device control commands can also be issued. The API allows simple way for a integrator to monitor devices statuses associated with single Shelly cloud account.
This is work in progress. Some URLs might change in the final variant of this documentation
Intended use and implementation example
This API is intended to be used by third party integrator that provide user account level integration with their services or by DIY enthusiast to experiment with some complex scripting of automations over their devices. This API is not intended to be used by cloud-to-cloud integrators that need to monitor multiple user accounts for device status events. If your use case leads to having multiple Real Time Events Web Sockets opened from single service point you should strongly consider the cloud-to-clod centric Integrator API
A working NodeJS/TypeScript example for consuming this API can be found at https://github.com/ALLTERCO/ushelly
Expected architecture on the integrator side
OAuth as per RFC6749
This API uses OAuth for authorization. Take a look at this 6 minutes video for OAuth workflow refreshment as the documentation assumes the reader is familiar with OAuth. For oauth to work a client id must be provided to authorization APIs. If you are a DIY enthusiast please use shelly-diy
as client id. If you're a third party integrator please contact us at support@allterco.com to obtain a integrator specific client id and callback URL matching regexp. Tokens obtained with shelly-diy
client id MIGHT be subject of rate restrictions.
OAuth details (work in progress)
Triggering the login process
The most secure for account owner login process follows this scenario:
Although a scenario where the handling of raw credentials from account owner and creating the Authorization Code is done entirely with Integrator's JS/Server side code is technically possible we strongly advise against it as account owners might have hard time trusting their raw credentials to UI from domain other than shelly.cloud
Our current OAuth authorization page (authorization endpoint) is at https://my.shelly.cloud/oauth_login.html
When you redirect the account owner to this page you need to specify in GET parameter client_id
the client id assigned to your system. For DIY enthusiast that just want to try something for their account this parameter should be shelly-diy
. Other important GET parameter that the authorization page considers is state
that is appended to the callback url. N.B. no URL encode is applied to it's value to allow easy GET parameters appending on the callback url, but if your value there contains &
unintentionally you should double urlencode your data or use some other kind of encoding. We also support the redirect_uri
parameter that is verified by the server to match the per client id preconfigured regular expression.
Obtaining the authorization code
When the authorization is complete the user browser will be redirected to client id specific callback URL with some GET parameters added:
state
from the authorization endpoint requestcode
the authorization code used later to obtain access tokens for actual API calls and WebSocket connection authentications.
Understanding the authorization code and access token
All OAuth tokens from Shelly cloud are JWT tokens. They are signed with a secret key but you can still decode them via your favorite JWT library or if you prefer to not depend on too much libraries you can just split the token by .
, take the middle part and base64decode it. Anyways you should get a JSON encoded object. The most important field there is user_api_url
that gives the designated server for the authorizing user. Subsequent HTTP calls/web socket connections MUST address the host mentioned in user_api_url
from the access token or authorization code.
Obtaining access token
To do any HTTP API calls or to be authenticated for WebSocket connection you need a access token a time limited representation of the account owner consent for account access. Obtaining the access code is a matter of calling HTTP API at https://<shelly_server>/oauth/auth
you need to provide these parameters:
client_id
- the client id assigned to your system orshelly-diy
for experimental purposesgrant_type
- this should be set tocode
code
- this should be set to URLEncoded authorization code obtained from the OAuth authorization process described above
On success you should get a JSON response - a object with most important field access_token
holding the access token you should use for authorization on all API calls. As mentioned above the access token is a JWT token with built in expiration date, If the JWT access token is expired the server will refuse to serve any requests and you will need to refresh your access token.
Common API considerations
Understanding the Device IDs
There is a contradiction in the way devices are identified in different parts of Shelly cloud. All user facing interfaces use the hexadecimal representation of the device ID while the device facing parts of the cloud prefer the decimal representation as string. There are also other devices (BLE, ZWave) that use device IDs starting with letter X
that are string-only and can't be converted to or from hex. APIs mix and match those representations. Both representations have pros and cons but any of them should uniquely identify a device. Keep in mind that hexadecimal representation MUST be prepadded with 0
to 6 or 12 characters length and if you want to check if two hexadecimal ids are equal you MUST transfer both to upper or lower case. In the example implementation project there is a function shelly_devid_hex
in shelly_types.ts for converting from decimal to hexadecimal representation. From hexadecimal to decimal representation a simple call to JS String(parseInt(hexid,16))
is enough. Keep in mind that all device IDs that appear in JSON MUST be strings.
Understanding the Device Generations
There are two distinct generations of Shelly devices gen1 and gen2 all devices in the same generation have similar data structures but there are some drastic differences between the two generations. It is reasonable to expect that the two generations will be handled in the third party code by different modules with almost no shared code so a quick way to identify the generation at top level logic is almost always present in the data structures. Gen1 devices are usually designated with "gen": "G1"
, gen2 devices with "gen": "G2"
and BLE devices with "gen": "GBLE"
. Sometimes a "virtual" might get reported with different designations. There are other generations of devices not discussed here such as ZWave line of devices. You should early filter out device generations that your code can't handle.
HTTP API
Shelly cloud has many callable HTTP API endpoints but we prefer to retain the ability to change any of their parameters and result formats. Not documenting them is a deliberate choice in this direction. Here we will document the bare minimum to allow functional communication with third party integrators at account level.
Authorization
All HTTP API calls should include Authorization : Bearer <ACCESS_TOKEN>
header to facilitate the OAuth authorization.
Interpreting API results
Most HTTP API calls return JSON formatted responses with common structure:
On success:
{
isok:true;
data: ....
}
On error:
{
isok:false;
errors: [
....
]
}
List of current/last known statuses of owned devices
/device/all_status?show_info=true&no_shared=true
This endpoint returns in data.devices_status
a object with fields named after all devices ID holding current status or last known status of all devices owned by the account. Here is an shortened example of a response:
{
"isok": true,
"data": {
"devices_status": {
"dc4f2276846a": {
.......
"_dev_info": {
"id": "dc4f2276846a",
"gen": "G1",
"code": "SHSW-1",
"online": false
}
},
.......
"84cca87c0144": {
.......
"_dev_info": {
"id": "84cca87c0144",
"gen": "G2",
"code": "SPSW-001PE16EU",
"online": true
}
},
.......
"1643370677417": {
.......
"_dev_info": {
"gen": "V1",
"id": 1643370677417,
"code": "THERMOSTAT",
"online": false
}
}
}
....
}
}
Few things to note as supported by the example above:
- All reported statuses have the
_dev_info
metadata attached. Where the generation (G1
,G2
,V1
) of the device, it's product code (SHSW-1
,SPSW-001PE16EU
andTHERMOSTAT
) and device id are explicitly stated. - There are other generations of devices beside gen1 (G1) and gen2 (G2). You should explicitly check the type and not assume "if it is not gen1 it's gen2"
- The key values in devices_status should be ignored as they are inconsistent, especially with the virtual thermostat devices, and will probably change at some point.
- For gen1 and gen2 devices the id in
_dev_info.id
is the hexadecimal id of the device. For other generations this might not be the case. online
key in_dev_info
indicates the online status as seen by the could. Whether the device is locally available is something different. Some battery devices on the other hand will be reported online while they are actually offline. Simply put, the cloud will refuse to handle commands for devices marked as offline. Translated to battery operated devices this means that when a battery device is marked offline the cloud will not try to store commands for delivery when the device connects back.- All other keys in
data
should be ignored
WebSocket API
To obtain real time data with device statuses a WebSocket connection to the proper shelly cloud server should be established. The url for the connection is wss://<shelly_cloud_server>:6113/shelly/wss/hk_sock?t=<ACCESS_TOKEN>
All messages coming from shelly cloud are JSON encoded. All messages coming from the server SHOULD have event
key holding string identifying the event type. you code should be prepared for ignoring unknown message formats and unknown event types.
Event Shelly:StatusOnChange
This event is send every time a status change is reported from a device.
{
"event": 'Shelly:StatusOnChange',
"device": {
"id": string,
"code": string,
"gen": string,
},
"status": {
....
};
}
- members of
device
identify the originating device status
holds the new status of the device.
Event Shelly:Online
This event is send every time the online status of a device changes.
{
"event": 'Shelly:Online',
"device": {
"id": string,
"code": string,
"gen": string,
},
"online": number
}
- members of
device
identify the device whose online status changes online
holds the new status of the device. if it is1
the device is considered online
Event Shelly:CommandResponse
This event is received when a command send to device gets resolved.
{
"event": 'Shelly:CommandResponse',
"deviceId":string,
"trid":number,
"data":unknown
...
}
trid
is the transaction id that led to this responsedeviceId
is the originating devicedata
is the response send from the device(or the error code send from the server)
Request Shelly:CommandRequest
The websocket client can request control operation to devices via shelly cloud. All request are send via common "event" structure:
{
"event":"Shelly:CommandRequest",
"trid":number,
"deviceId":string,
"data":{
"cmd":string
.....
}
}
trid
is a transaction id number linking request message to response message. The response arrives viaShelly:CommandResponse
event. This is transparent to the cloud, best practice is to use atomically incremented counter that wraps around to 0 at some sane value.deviceId
is the target device to handle the command.data
holds the command payloaddata.cmd
identifies the command with other keys indata
being specific to the command itself
Implemented command requests:
relay
The command changes a relay state.
{
"cmd":"relay",
"params":{
"turn":string,
"id":number
}
}
params.turn
ison
,off
ortoggle
params.id
identifies the relay channel to change, this starts from0
. It must be present even for single channel relays with value0
light
The command changes a light controller state.
{
cmd: 'light',
params: {
id:number
turn?: string,
mode?: string,
timeout?: number,
red?: number,
green?: number,
blue?: number,
white?: number,
gain?: number,
brightness?: number,
effect?: number,
temp?: number,
}
}
- All params but
id
, the channel to setup, are optional. The channel number starts from 0 and must be present even for single channel devices with value0
- Not all models support all parameters. Take a look at each device local control description to deduce what is possible for what model
turn
ison
,off
ortoggle
gain
,brightness
are numbers in rage 0 to 100, 100 indicating "full power"effect
is number in rage 0 to 6, model specific- all colors are in rage 0 to 255 to represent the usual RGB colorspace
temp
is light temperature in Kelvins
roller
The command starts a roller/cover direction or stops it
{
cmd: 'roller',
params: {
go: string,
duration:number,
id:number
}
}
go
isup
,down
orstop
id
identifies the roller/cover channel to change, this starts from0
. It must be present even for single channel roller/cover with value0
duration
is optional, in seconds and works only forgo
=up
andgo
=down
roller_to_pos
The command sends a roller/cover to specific position.
{
cmd: 'roller_to_pos',
params: {
pos: number,
id:number
}
}
- The roller/cover must be calibrated for this command to be accepted by the device
pos
is the position expressed in percent in range 0 to 100id
identifies the roller/cover channel to change, this starts from0
. It must be present even for single channel roller/cover with value0