Omahub
← All plugins
A

SmartThings

by artur-hash

Every device in a SmartThings account from the bar, grouped by room, each showing the controls it actually publishes.

Security review

Review recommended · 8 findings

Deterministic scan — not a security guarantee

Medium
Risk level
Medium
Analyzed commit
6023f88
Scanned
1 month ago
  • medium package_manager …/workflows/ci.yml:9

    System package manager operation.

    apt-get install -y jq curl libsecret-tools
  • medium package_manager Panel.qml:548

    Global npm package installation.

    npm install -g @smartthings/cli."
  • medium package_manager scripts/setup.sh:50

    Global npm package installation.

    npm install -g @smartthings/cli${RESET}"
  • medium package_manager scripts/setup.sh:53

    Global npm package installation.

    npm install -g @smartthings/cli || fail "npm install failed. If it was a permissions error, set a user prefix (npm config set prefix ~/.local) and try again"
  • medium package_manager bin/smartthings:448

    Global npm package installation.

    npm install -g @smartthings/cli\n'
  • medium package_manager bin/smartthings:454

    Global npm package installation.

    npm install -g @smartthings/cli && smartthings locations\n'
  • Command runs with sudo, elevating the process beyond the plugin environment.

    sudo apt-get install -y jq curl libsecret-tools
  • Docs package_manager README.md:68

    Global npm package installation.

    npm install -g @smartthings/cli

Automated analysis only — not a security guarantee.

AI advisory review

No obvious issues detected

Language-model assessment · ~deepseek/deepseek-v4-flash-latest — advisory only

Low
AI risk level
Low
Recommendation
install
Model
~deepseek/deepseek-v4-flash-latest
Analyzed commit
6023f88
Reviewed
1 month ago

The plugin is a well-documented SmartThings bar widget with a hardened backend that avoids storing its own credentials and clamps all remote data. The flagged npm global installs and sudo in CI are user-initiated setup steps or CI-only operations, not runtime behavior, and the setup script asks for confirmation before installing anything. No obfuscation, persistence, or destructive actions were found.

  • The setup script installs the SmartThings CLI globally via npm, which is a supply-chain dependency outside the plugin's control, but it is user-initiated and clearly documented.
  • The plugin reads the SmartThings CLI's credential file and relies on the CLI for token refresh; a compromised CLI could expose the token, but this is an inherent dependency and the plugin does not add its own credential storage.
How this check works

This review combines the deterministic scan (the rule-based results above) with an independent look at the plugin's code by a language model. The model reads a trimmed sample of the repository's files, the manifest, and the README, then gives a plain-language risk level and a recommendation: install (no notable danger), review (look closer first), or avoid (clearly dangerous).

It runs on the same analyzed commit as the deterministic scan and is strictly advisory — it is not a security guarantee and never blocks a plugin by itself. A human moderator still reviews plugins before they are listed.

AI advisory only — automated analysis, not a security guarantee.

Install
$ omarchy plugin add https://github.com/artur-hash/omarchy-smartthings --enable
Hardware #bar #quickshell

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.