Repository navigation
HTTP API
ShellyElevate runs a small HTTP server on port 8080. It's on by default and there's no authentication, so this is a LAN-only thing. The port isn't configurable.
All responses are JSON. Most of them include a success boolean. POST bodies are JSON unless noted.
Quick sanity check:
curl http://<device-ip>:8080/If you get a JSON blob back, you're good.
Returns the basics: package, version, detected model, and how many buttons/inputs the model has.
{
"name": "me.rapierxbox.shellyelevatev2",
"version": "3.0.0",
"modelName": "BLAKE",
"proximity": "true",
"numOfButtons": 4,
"numOfInputs": 1
}modelName matches the internal name in Supported Devices.
Dump every setting key as a flat JSON object. Useful for backups.
curl http://<device-ip>:8080/settingsWrite one or more settings. Include only the keys you want to change, or include everything to restore a backup.
curl -X POST http://<device-ip>:8080/settings \
-H "Content-Type: application/json" \
-d '{
"mqttEnabled": true,
"mqttBroker": "tcp://192.168.1.50",
"mqttPort": 1883,
"mqttUsername": "homeassistant",
"mqttPassword": "yourpassword"
}'Every valid key is in Configuration Reference.
A few details worth knowing:
- Sending
nullfor a key removes it. - Types are inferred from JSON, but an existing key keeps the type it's already stored as: if a key is already an
int, a value like50.5is rounded and written back asintrather than switching the type (this avoids crashing readers that expect an int). New keys follow the JSON: a whole number in int range becomesint, a decimal becomesfloat.dynamicTempOffsetBaselineanddynamicTempOffsetKare always float. - A JSON array of strings becomes a
StringSet. Mixed-type arrays are silently ignored. - After the write, the settings change is broadcast internally. Running components react immediately, you don't need to reboot.
# read
curl 'http://<device-ip>:8080/device/relay?num=0'
# returns {"success": true, "state": true}
# write
curl -X POST http://<device-ip>:8080/device/relay \
-H "Content-Type: application/json" \
-d '{"num": 0, "state": true}'On the write, num can also be passed as a query param. Returns the resulting state.
| Endpoint | Returns |
|---|---|
GET /device/getTemperature |
{"temperature": 23.5} |
GET /device/getHumidity |
{"humidity": 45.2} |
GET /device/getLux |
{"lux": 120.5} |
GET /device/getProximity |
{"distance": 5.0} (fails on models without proximity) |
curl -X POST http://<device-ip>:8080/device/wake # wake the screen
curl -X POST http://<device-ip>:8080/device/sleep # start screensaver
curl -X POST http://<device-ip>:8080/device/reboot # reboot the devicereboot has a 20-second cooldown after startup. If you call it too early it silently refuses (returns success: false and shows a toast). This stops the device entering a reboot loop on a crashing boot.
curl 'http://<device-ip>:8080/device/night_mode' # read -> {"success": true, "state": false}
curl -X POST http://<device-ip>:8080/device/night_mode \
-H "Content-Type: application/json" -d '{"state": true}' # setcurl -X POST http://<device-ip>:8080/device/settings # open the settings screen on the device
curl -X POST http://<device-ip>:8080/device/close # exit the app (System.exit) ~100ms after replyingclose is mostly useful for scripted restarts (the app is relaunched by the kiosk watchdog / on boot). It's a hard exit, not a graceful one.
If you have a Shelly Dimmer backplate wired up over UART:
# status (also reports power if measured)
curl http://<device-ip>:8080/device/dimmer{
"success": true,
"on": true,
"brightness": 75,
"not_dimmable": false,
"not_calibrated": false,
"overheat": false,
"overcurrent": false,
"power": 31.2,
"voltage": 230,
"current": 0.135
}Set on/off or brightness (0 to 100):
curl -X POST http://<device-ip>:8080/device/dimmer \
-H "Content-Type: application/json" -d '{"brightness": 50}'
curl -X POST http://<device-ip>:8080/device/dimmer \
-H "Content-Type: application/json" -d '{"on": false}'Returns 404 if no dimmer is detected on the UART bus.
curl http://<device-ip>:8080/device/freeReturns the parsed output of free -m. Useful for diagnosing weird crashes; these are constrained devices.
Reload the page. No body needed.
Execute arbitrary JS in the WebView.
curl -X POST http://<device-ip>:8080/webview/inject \
-H "Content-Type: application/json" \
-d '{"javascript": "document.body.style.background=\"red\""}'This is the same channel the JavaScript Interface uses, in reverse.
Media playback is gated behind mediaEnabled: true in settings. Until that's on, every /media/* endpoint returns 400.
curl -X POST http://<device-ip>:8080/media/play \
-H "Content-Type: application/json" \
-d '{
"url": "http://example.com/sound.mp3",
"music": true,
"volume": 0.5
}'-
music: true: uses the "music" player. Loops, can be paused/resumed. -
music: false: "effect" player. One-shot. Pauses music while it plays. -
volumeis0.0to1.0.
curl -X POST http://<device-ip>:8080/media/pause
curl -X POST http://<device-ip>:8080/media/resume
curl -X POST http://<device-ip>:8080/media/stop
curl http://<device-ip>:8080/media/volume
curl -X POST http://<device-ip>:8080/media/volume \
-H "Content-Type: application/json" -d '{"volume": 0.8}'Pause/resume only affect the music player, not effects.
- Wrong HTTP method on an endpoint returns 500 with
{"success": false}. - Bad inputs (missing query param, malformed body) return 400 with an error message.
- Media-disabled returns 400 with
{"error": "Media disabled"}. - Dimmer-missing returns 404 with
{"error": "Dimmer not attached"}.
Don't. There's no authentication. If you need remote access, put the device behind a VPN or use MQTT over a TLS-enabled broker.
Setup
Integration
Features
Reference