Skip to main content

Common Device Traits

HTTP Compatibility Layer#

Shelly-NG supports a minimal subset of HTTP endpoints used specifically for device control. The reason for including the HTTP compatibility layer in Shelly-NG is to give our clients smooth transition path from the Classic Shelly devices to Gen2 by providing the ability to control the new devices through a familiar mechanism, namely the Classic Shelly API.

All shellies from both Gen1 and Gen2 families expose the /shelly endpoint, useful for discovery of devices and their features and capabilities.

Functional components also expose their own set of compatibility HTTP resources, documented on their respective pages, for example /relay/N

mDNS#

Gen2 shellies advertise themselves the Multicast Domain Name System (mDNS), also known as Bonjour. Devices advertise two types of services:

  • _http._tcp - a web server serving on port 80. This service is also advertised for Gen1 shellies
  • _shelly._tcp - service instance specific to Gen2 Shelly devices.

In the TXT record for both services devices include gen=2. This key can be used to distinguish from Gen1 shellies if discovery is done using the _http._tcp service instance.

Authentication#

Communication through HTTP and Websocket channels is secured by a digest authentication mechanism using the SHA256 hmac algorithm as defined in RFC7616.

When enabled, all communication is protected except:

  • Communication over MQTT
  • Communication over UART
  • the RPC method Shelly.GetDeviceInfo
  • the HTTP endpoint /shelly

Setting Authentication Credentials for a Device#

Authentication can be enabled by setting authentication details through the RPC method Shelly.SetAuth. A pre-calculated ha1 parameter is expected - this is the result of SHA256(user:realm:password). Please refer to RFC7616 for further details on the mechanism.

Example:

HA1 calculation with JavaScript
let crypto = require('crypto');
let username = 'admin'; // always
let password = 'mysecretpassword';
let realm = 'shellymodel-XXXXXXXXXXXX';
let auth_parts = [username, realm, password];
let ha1 = crypto.createHash("sha256").update(auth_parts.join(':')).digest("hex");
console.log(ha1);

Authentication process#

sequenceDiagram; autonumber; Client->>Device: Requesting protected resource.; Device->>Client: Error 401 with authentication challenge.; ; Client->>Device: Requesting protected resource with credentials.; Device->>Client: Result of the successful request.;

Steps in the process:

  1. Client requests a protected resource without providing credentials.
  2. Server response containing error 401 (unauthorized) is received.
  3. Client requests the same protected resource but this time providing credentials.
  4. The request is successful and access to the resourse is granted.

When communicating over HTTP, this process must be repeated for each request you send to the device. However, for communication over websocket there is no need to pass through steps 1 and 2 more than once: you need to construct the auth object only once(see more about that below) and then use it for all consecutive requests to that device.

Error 401 and the Authentication Challenge#

The error 401 response mentioned in step 2 contains information about:

  • auth_type: string, digest
  • nonce: number, random or pseudo-random number to prevent replay attacks
  • nc: number, nonce counter (returned only through websocket channel). It has value of 1 if it is not available in the response
  • realm: string, device_id of the Shelly device
  • algorithm: string, SHA-256

Authentication over HTTP#

When communicating through a HTTP channel this information can be found in the WWW-Authenticate header of the response.

Example:

Request
curl -X POST -i -d '{"id":1, "src":"user_1", "method":"Shelly.GetStatus"}' http://${SHELLY}/rpc
Response
HTTP/1.1 401 Unauthorized
Server: Mongoose/6.18
Content-Type: application/json
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: *
Content-Length: 0
Connection: close
WWW-Authenticate: Digest qop="auth", realm="shellypro4pm-f008d1d8b8b8", nonce="60dc59c6", algorithm=SHA-256

Authentication over Websocket#

When communicating through a websocket channel, the response contains error frame where the above attributes can be found.

Example:

Request
curl -X POST -d '{"id":1, "src":"user_1", "method":"Shelly.DetectLocation"}' http://${SHELLY}/rpc
Response
{
"id": 1,
"src": "shellypro4pm-f008d1d8b8b8",
"dst": "user_1",
"error": {
"code": 401,
"message": "{\"auth_type\": \"digest\", \"nonce\": 1625038762, \"nc\": 1, \"realm\": \"shellypro4pm-f008d1d8b8b8\", \"algorithm\": \"SHA-256\"}"
}
}

Successful Request with Authentication Details#

In order to make the request work properly, an additional auth JSON object should be added to the request frame. It should contain:

  • realm: string, device_id of the Shelly device. Required
  • username: string, must be set to admin. Required
  • nonce: number, random or pseudo-random number to prevfent replay attacks, taken from the error messsage. Required
  • cnonce: number, client nonce, random number generated by the client. Required
  • response: string, encoding of the string ha1 + ":" + nonce + ":" + nc + ":" + cnonce + ":" + "auth" + ":" + ha2 in SHA256. Required
    • ha1: string, "user:realm:password" encoded in SHA256
    • ha2: string, "dummy_method:dummy_uri" encoded in SHA256
  • algorithm: string, SHA-256. Required

Example:

Request1
curl -X POST -d '{"id":1, "src":"user_1", "method":"Shelly.DetectLocation", "auth":
{"realm": "shellypro4pm-f008d1d8b8b8", "username": "admin", "nonce": 1625038762,
"cnonce": 313273957, "response": "eab75cbbd7acdb7082164cb52148cfbe351f28bf80856f93a23387c6157dbb69",
"algorithm": "SHA-256"}}' \
http://${SHELLY}/rpc

Or just:

Request2
curl --digest -u admin:mypass http://${SHELLY}/rpc/Shelly.DetectLocation
Response
{
"id": 1,
"src": "shellypro4pm-f008d1d8b8b8",
"dst": "user_1",
"result": {
"tz": "Europe/Sofia",
"lat": 42.67236,
"lon": 23.38738
}
}

SSL support for outbound connections#

Shelly-NG provides secure socket support for:

  • MQTT with support for CA bundle selection
  • Webhooks (only built in CA bundle supported, support for CA selection will be added in 0.8.0)
  • The RPC method HTTP.GET

Internally, TLS is provided by the industry-standard mbedtls library. Certificate validation is supported, and users can choose between no validation, a built-in CA bundle and a user-provided CA bundle.

Custom CA PEM Bundle#

A CA bundle which covers many of the widely-used SSL providers is already available for Shelly-NG. In cases where the built-in bundle doesn't include a needed certificate, or cases in which self-signed server certificates are used, a second CA bundle can be added by the user. For outgoing TCP connections with TLS support, the CA bundle can be chosen by:

  • "*" - use SSL but disable certificate validation
  • "ca.pem" - use the default CA bundle
  • "user_ca.pem" - use a bundle uploaded by Shelly.PutUserCA

Debug Logs#

Ability to stream debug logs from a device over MQTT, websocket or UDP to diagnose problems is supported. Information about your current configuration of this feature can be found through the method Sys.GetConfig. Log streams are disabled by default. To change the configuration and enable logging, use the RPC method Sys.SetConfig.

Logs over MQTT#

When enabled, debug log is published to the topic shellydevicemodel-XXXXXXXXXXXX/debug/log.

Example:

Example log snippet
shellypro4pm-f008d1d8b8b8 0 1630591789.944 2|mgos_sys_config.c:232 Loading conf3.json
shellypro4pm-f008d1d8b8b8 1 1630591790.208 2|mgos_sys_config.c:232 Loading conf9.json
shellypro4pm-f008d1d8b8b8 2 1630591790.346 2|mgos_sys_config.c:232 Loading conf3.json
shellypro4pm-f008d1d8b8b8 3 1630591790.732 2|mgos_sys_config.c:174 Saved to conf9.json
shellypro4pm-f008d1d8b8b8 4 1630591790.750 2|shelly_notifications:79 NotifyEvent: 'localweb266' (WS_in) -> ok? 1
shellypro4pm-f008d1d8b8b8 5 1630591790.758 2|shelly_notifications:79 NotifyEvent: 'shelly.cloud' (SHC) -> ok? 1
shellypro4pm-f008d1d8b8b8 6 1630591790.768 2|shelly_notifications:79 NotifyEvent: 'shellypro4pm-f008d1d8b8b8/events' (MQTT) -> ok?

Logs over Websocket#

danger

Access to log streams over websocket is not restricted, even when authentication is enabled!

When enabled, device log is streamed to ws://SHELLY_HOST/debug/log (for example, ws://10.33.52.133/debug/log). When disabled, logging is stopped and all open websocket debug connections are closed. Up to three websocket debug connections can be opened simultaneously.

The log is streamed as JSON objects that have the following properties:

  • ts: number, unix timestamp (in UTC)
  • level: number, log level. Range of values: {0 -> error, 1 -> warn,2 -> info, 3 -> debug, 4 -> verbose debug}
  • data: string, the log message

Example:

{
"ts": 1630322411.725,
"level": 2,
"data": "shelly_switch.cpp:1096 change voltage\n"
}

Logs over UDP#

When enabled, device log is streamed to the address (HOST:PORT) specified in the configuration of the System component of your device.

You can observe the streamed log using:

mos console --port udp://HOST:PORT

Example:

Example log snippet
[Sep 2 17:28:00.811] shellypro4pm-f008d1d8b8b8 66 366.319 2 2|shelly_switch.cpp:1096 change aenergy
[Sep 2 17:28:00.813] shellypro4pm-f008d1d8b8b8 67 366.327 2 2|shelly_notifications:79 NotifyStatus: 'localweb247' (WS_in) -> ok? 1
[Sep 2 17:28:00.822] shellypro4pm-f008d1d8b8b8 68 366.338 2 2|shelly_notifications:79 NotifyStatus: 'shellypro4pm-f008d1d8b8b8/events' (MQTT) -> ok? 1
[Sep 2 17:28:00.822] shellypro4pm-f008d1d8b8b8 69 366.352 2 2|shelly_switch.cpp:1096 change aenergy
[Sep 2 17:28:00.823] shellypro4pm-f008d1d8b8b8 70 366.361 2 2|shelly_notifications:79 NotifyStatus: 'localweb247' (WS_in) -> ok? 1