SmartThings
<p align="center"> <img src="preview.png" alt="The SmartThings panel: devices, and one device's controls" width="720"> </p>Every device in a SmartThings account from the Omarchy bar — grouped by room, each one showing the controls it actually publishes. The bar shows how many are on.
Nothing here is keyed on a device type, model or vendor. A control is earned by a published capability, so a device offering different values shows different buttons with no code change, and a device that publishes nothing usable shows no control rather than a dead one — a television lists no input sources while it is off, and the input picker simply is not there until it does.
What it controls
Whatever the account publishes, from this set:
| Capability | Control |
|---|---|
switch |
power |
switchLevel |
level |
thermostatCoolingSetpoint |
target temperature, range read from the device |
airConditionerMode, airConditionerFanMode, fanOscillationMode, custom.airConditionerOptionalMode |
mode, fan, swing, preset |
audioVolume, audioMute |
volume, mute |
mediaPlayback, mediaTrackControl, mediaInputSource |
transport, track, input |
Read-only: temperature, humidity, a feels-like figure from the NWS heat index, illuminance, presence, battery, channel.
What it does not do
Scenes and automations, renaming devices or moving rooms, and vendor extras such as Samsung's art and ambient modes. Generality is the point; a Samsung-specific control belongs in a Samsung-specific plugin.
Requirements
- Omarchy 4 (Quattro)
curl,jq,libsecret(secret-tool) — all present on a default install- A SmartThings personal access token
Install
omarchy plugin add https://github.com/artur-hash/omarchy-smartthings.git --enable
Then add the widget to the bar from the shell's own widget settings.
Setup
~/.config/omarchy/plugins/io.github.artur-hash.smartthings/scripts/setup.sh
It checks for node, installs the SmartThings CLI after asking, opens a
browser to log in, and finishes by running this plugin's own doctor so you
know it worked before you go looking at the bar.
Omarchy never executes plugin code at install time, which is the right call, so
nothing here runs on its own — you run this once.
By hand, if you prefer:
npm install -g @smartthings/cli
smartthings locations
The panel also has a Log in button once the CLI is installed, which does the same thing as that second command.
Install the CLI globally, not through npx. This plugin stores no
credential of its own and never touches the keyring — it reads the session the
CLI keeps, the way other tools read gcloud's or gh's, and that session
renews itself. But the CLI only renews it when one of its own commands runs, so the plugin has
to be able to find it. PATH is not enough on its own — the shell takes its
environment from the session, not from your terminal's rc — so it also looks
where npm, mise, nvm, volta and asdf put things. If it cannot find the binary
anywhere, the panel and doctor both say so rather than letting the session
work for a day and then quietly stop renewing.
Why there is no token to paste
SmartThings expires a personal access token 24 hours after it is created. Tokens issued before 30 December 2024 could last fifty years; new ones cannot. A bar widget that asks for a fresh credential every morning is not one anybody keeps, so that path was removed rather than offered as a fallback nobody should choose.
The other route — having the plugin register its own OAuth app — is closed to a whole class of user, and fails in a way that wastes an evening before it explains itself. Authorising a third-party app means installing it into a location you own. If someone else set up the home and shared it with you, you own none: the consent screen answers "at least one location is required" on mobile, and the considerably less helpful "it looks like you have not set up a SmartThings account" on desktop. Your devices are right there and read and control perfectly; only app authorisation is closed.
The CLI sidesteps it because its own client installs with no location at all.
Rooms
Room names need the location read scope, which the CLI session carries.
Devices are grouped under their room, and when the account holds more than one
location the location leads the heading — Home · Attic — because two
places can each have a room by the same name. A device in no room gets its own
heading rather than sitting silently under the one above it.
What the backend trusts
Nothing the network says, beyond its shape.
omarchy-shell is one long-lived process shared by every widget, and the QML
side collects this helper's whole stdout. A response with no ceiling is
therefore a way for whatever answers on the socket to exhaust the shell, not
just this plugin. The ceiling is enforced while the response is arriving rather
than after it has been read, and an overflow fails closed — a truncated body is
never parsed, guessed at, or passed on.
Past that, every value forwarded to the panel is clamped: strings to 128 characters, lists to 64 entries, the device list to 200 rows.
Writes are never assumed. The API answers 200 for a command it refuses, and a
device answers COMPLETED for a command it then silently drops — an air
conditioner ignores a setpoint while it is off. Every write is read back a few
seconds later, and the panel says when the device did not apply it rather than
leaving a button claiming a state nothing checked.
Requests are the scarce resource
There is no bulk status endpoint: GET /devices/status and
GET /devices/health both answer HTTP 400, so status costs one request per
device. This shapes the design more than anything else.
- The device list is structure only, read once per session.
- Status is fetched only for devices whose state is on screen.
- Reachability is a separate call the panel makes only where it changes what is shown.
- Reads are sequential inside one backend process, never a fan-out of concurrent children: the limit that bites is a burst limit.
- Polling follows attention — twenty seconds with the panel open, ninety without — and backs off exponentially on HTTP 429.
Diagnostics
~/.config/omarchy/plugins/io.github.artur-hash.smartthings/bin/smartthings doctor
Reports the dependencies, whether a token is stored (redacted), how many devices are visible, and whether the token carries the location scope.
Removal
omarchy plugin remove io.github.artur-hash.smartthings
Deleting the directory is enough. smartthings token clear removes the stored
token.
Tests
bash tests/test_smartthings.sh # backend, against a faked API and keyring
node tests/test_model.js # the capability registry and everything pure
License
MIT.