-
-
Notifications
You must be signed in to change notification settings - Fork 24
JavaScript Interface
If the page running inside the WebView is something you control (a custom dashboard, a Lovelace card you wrote, a static HTML page), you can talk to the hardware directly from JavaScript instead of going over HTTP or MQTT. ShellyElevate injects an object called ShellyElevate into every loaded page.
A small read-only subset is always available. The more interesting stuff (events, screensaver control) needs the Extended JavaScript Interface turned on in settings (extendedJavascriptInterface). It's off by default because exposing relay control to anything that ends up in the WebView is the kind of decision you want to make on purpose.
The page might also be opened in a normal browser for development, so a defensive check is reasonable:
if (typeof ShellyElevate === 'undefined') {
console.log("Not running inside ShellyElevate");
return;
}
console.log("Device:", ShellyElevate.getDevice());
console.log("Extended:", ShellyElevate.getExtendedJavascriptInterfaceEnabled());ShellyElevate.getTemperature() // float, °C
ShellyElevate.getHumidity() // float, %
ShellyElevate.getLux() // int, lux
ShellyElevate.getProximity() // float, cm (last reported)These return the same calibrated values that go out over MQTT and HTTP. Re-reading them in a tight loop is fine, they're cached.
ShellyElevate.getRelay(0) // returns true / false
ShellyElevate.setRelay(0, true) // turn relay 0 onRelay indices depend on the model (see Supported Devices). Setting a relay on a model that doesn't have one is a no-op rather than an error.
ShellyElevate.getScreenBrightness() // 0-255 (the setting value)
ShellyElevate.getCurrentScreenBrightness() // 0-255 (whatever is on the hardware right now)
ShellyElevate.setScreenBrightness(128)The "current" version is useful while a brightness animation is mid-fade. It gives you the actual value being written to the backlight node.
ShellyElevate.sleep() // start screensaver now
ShellyElevate.wake() // stop it
ShellyElevate.getScreenSaverRunning() // bool: is it active right now
ShellyElevate.getScreenSaverEnabled() // bool: is the feature on
ShellyElevate.getScreenSaverId() // int: 0/1/2, see Screensavers page
ShellyElevate.setScreenSaverEnabled(true)
ShellyElevate.setScreenSaverId(2) // 2 = clock + date
ShellyElevate.keepScreenAlive(true) // suppress idle timer
ShellyElevate.keepScreenAlive(false) // resume itkeepScreenAlive(true) is what you want during a video playback or any kind of "user is watching this, don't dim" situation. Don't forget to flip it back off.
ShellyElevate.getDevice() // model SKU
ShellyElevate.getExtendedJavascriptInterfaceEnabled() // bool
ShellyElevate.isInForeground() // boolisInForeground is the combined "app is in foreground AND screen is on". A useful gate for animations or polling.
You wire up a JavaScript function and tell ShellyElevate to call it when something happens.
function handleScreenOn() { console.log("woke up"); }
function handleScreenOff() { console.log("going to sleep"); }
function handleButton(id) { console.log("button", id); }
ShellyElevate.bind("onScreenOn", "handleScreenOn");
ShellyElevate.bind("onScreenOff", "handleScreenOff");
ShellyElevate.bind("onButtonPressed", "handleButton");The string is the name of a global function. ShellyElevate.bind doesn't take a function reference, it takes the name to look up at call time. Define your handlers on window and you're fine.
Available events:
| Event | Args | Fires when |
|---|---|---|
onScreenOn |
none | display wakes |
onScreenOff |
none | display sleeps |
onScreensaverOn |
none | screensaver started |
onScreensaverOff |
none | screensaver stopped |
onMotion |
none | proximity detected (throttled) |
onButtonPressed |
buttonId (int) |
physical button on the front of the device |
Without the extended interface enabled, bind() silently does nothing. That's intentional so a page can call it without checking, and gracefully degrade to no events.
A dashboard widget that toggles the kitchen relay on button 1, ramps up the brightness on button 2, and pauses while a video is playing:
(function () {
if (typeof ShellyElevate === 'undefined') return;
window.handleButtonPress = function (id) {
switch (id) {
case 1:
const s = ShellyElevate.getRelay(0);
ShellyElevate.setRelay(0, !s);
break;
case 2:
const b = ShellyElevate.getScreenBrightness();
ShellyElevate.setScreenBrightness(Math.min(255, b + 50));
break;
}
};
ShellyElevate.bind("onButtonPressed", "handleButtonPress");
const video = document.querySelector("video");
if (video) {
video.addEventListener("play", () => ShellyElevate.keepScreenAlive(true));
video.addEventListener("pause", () => ShellyElevate.keepScreenAlive(false));
video.addEventListener("ended", () => ShellyElevate.keepScreenAlive(false));
}
setInterval(() => {
const t = ShellyElevate.getTemperature();
const h = ShellyElevate.getHumidity();
document.getElementById("temp").textContent = t.toFixed(1) + "°C";
document.getElementById("hum").textContent = h.toFixed(1) + "%";
}, 5000);
})();The repo ships with a test page at app/src/main/assets/jsinterfacetest.html that exercises every method and event. If you're building a dashboard, that's a useful reference to skim. It's intentionally ugly, but it shows the shape of each call.
You can also inject JavaScript from outside the WebView using HTTP API, at POST /webview/inject. Same context as in-page JS, so the ShellyElevate object is available there too. Handy for prototypes.
Setup
Integration
Features
Reference