Skip to main content

Leviton Decora Smart Wi-Fi (My Leviton)

GEM driver for Leviton Decora Smart Wi-Fi switches, dimmers, and plug-in modules via the MyLeviton (my.leviton.com) cloud REST API. Covers the first-generation Decora Smart Wi-Fi line — DW15S, DW15P, DW6HD, D215S, D26HD, D215P, and the outdoor DW15R plug — plus the same generation's fan controllers.

Feature coverage:

  • Discovery of every switch on the account (get_devices)
  • On / Off / Toggle
  • set_level for dimmers (0-100, with automatic off at 0)
  • Periodic polling → mirrors power onto the zone state attribute and brightness onto the zone level
  • Optional pin to a specific residence when the account owns more than one home
  • Raw discovery of residences (get_residences) for multi-home accounts

Second-generation "Decora Smart WiFi-2 / HomeKit" devices use a different platform and are NOT covered here. If the device shows up under "Leviton Homekit" or "Decora Smart" (rather than "My Leviton") in the mobile app switcher, this driver won't reach it.

Prerequisites

  • MyLeviton account. The account must be fully verified (email confirmation completed) and hold the target switches. If you can toggle the switches from the MyLeviton mobile app in "My Leviton" mode, the account is good.
  • Two-factor authentication OFF. The driver does not solve TOTP prompts inline. Either disable 2FA on the MyLeviton account, or run a small helper that mints a session token by another means and bypass the login step.
  • A dedicated automation account is a good idea. Reset the password on a dedicated MyLeviton account and share the residence to it, rather than embedding a homeowner's personal credentials in a controller.

Setup steps

  1. Enroll switches in MyLeviton. Pair each switch through the MyLeviton mobile app, verify the app can toggle it, and note whether the switch is a dimmer (supports brightness) or a plain on/off model.
  2. Add the device in GEM. In Devices, create a new device with driver leviton_decora. Fill in:
    • email — the MyLeviton account email
    • password — the MyLeviton account password (stored encrypted)
    • Leave api_base on the default. Only set residence_id if the account has multiple residences and you want to pin the driver to one.
  3. Discover switches. Open the device's Script Console tab and run:
    await gem.command({device: <device_id>, action: 'get_devices'})
    The response is an array of iotSwitches. Note the numeric id, name, power, and canSetLevel for each — canSetLevel: true means it's a dimmer.
  4. Create GEM zones. For each switch you want to control from GEM, create a zone under a lighting subsystem. Set zone.address to the numeric iotSwitch id. Set the zone type to a dimmer for canSetLevel: true devices; on/off otherwise.
  5. Verify. From the Script Console, send on, off, and (for dimmers) set_level with a value like 50. Confirm the switch responds and that the zone state / level attributes track the physical state within a poll cycle (default 30s).

Attributes

Device — required

NameTypeDescription
emailstringMyLeviton account email. Same address used to sign into the mobile app.
passwordstring (secure)MyLeviton account password. Stored encrypted; used only to mint session tokens against my.leviton.com.

Device — optional

NameTypeDefaultDescription
api_basestringhttps://my.leviton.com/apiMyLeviton API endpoint. Only change if Leviton publishes a region-specific or partner endpoint.
residence_idstringWhen the account has multiple residences, pin the driver to one by setting the residence id (from get_residences). Leave blank to include all residences the account can see.
status_intervalint (ms)30000How often to refresh switch state from the cloud. Minimum 10s — Leviton throttles below that.
request_timeoutint (ms)15000HTTP request timeout per call.

Zone — address

FieldDescription
addressThe MyLeviton iotSwitch numeric id (e.g. 1234567). Discover via get_devices.

Commands

NameArgsDescription
onaddressTurn the switch on. For dimmers, the cloud restores the last-set brightness.
offaddressTurn the switch off.
set_leveladdress, levelSet brightness for a dimmer (0-100). Auto-off when level=0, auto-on when level>0. Rejected for non-dimmers based on the cached canSetLevel flag.
toggleaddressToggle the switch based on last-known cached state (falls back to a cloud fetch if the zone isn't cached).
get_statusaddressReturn the current power / brightness / model for one switch.
get_devicesList every iotSwitch across the residences the account can see.
get_residencesList residences visible to the account. Use to populate residence_id.
refresh_tokenForce a fresh MyLeviton login now. Rarely needed — the driver re-authenticates automatically on 401 responses.

Auth model

The MyLeviton API is opaque-token based:

  1. POST /api/Person/login with {email, password, clientId}.
  2. Response includes id (the session token) and userId.
  3. Subsequent calls pass the raw token in an Authorization header — no Bearer prefix. This matches the MyLeviton mobile app.
  4. GET /api/Person/{userId}/residentialPermissions yields one or more residentialAccountIds.
  5. GET /api/ResidentialAccounts/{accountId}/Residences yields residence ids.
  6. GET /api/Residences/{residenceId}/iotSwitches yields the switch list.
  7. PUT /api/IotSwitches/{id} with {power, brightness} sends control commands.

Session tokens are long-lived (weeks) but do eventually expire; the driver re-logs in transparently on 401.

Known limitations

  • Cloud-only. Leviton does not expose a documented local API for the Decora Smart Wi-Fi (My Leviton) generation. All commands round-trip through the MyLeviton cloud. When the internet or the cloud is down, the switches keep working locally but GEM cannot control them.
  • No push channel. MyLeviton has no third-party WebSocket or webhook. Manual state changes at the switch face show up on the next poll (default 30s).
  • No scene / group control. MyLeviton scenes are visible on the account but not exposed as a first-class verb here. Use raw_get on /Residences/{id}/scenes for advanced access.
  • No color / colour temperature. The first-generation platform is white-only (or on/off). Newer color-tunable Decora products are on a different platform.
  • 2FA-enabled accounts are not supported inline. Bootstrap a session token separately if the account requires TOTP on login.
  • Undocumented rate limits. Poll intervals below 10s frequently produce 429 responses.

Troubleshooting

SymptomCheck
401 on every request immediately after connectVerify the email/password against the MyLeviton mobile app. If 2FA is enabled, disable it or bootstrap a token by another means.
get_devices returns an empty listThe account may have zero switches enrolled on the primary residence. If the account has multiple homes, set residence_id, or first run get_residences to enumerate.
set_level returns device does not support brightnessThe switch model is not a dimmer (e.g. DW15S, DW15P). Use on/off instead.
429 Too Many RequestsRaise status_interval. MyLeviton silently throttles chatty accounts.
Command completes but the switch does not reactThe switch may be offline (Wi-Fi drop, breaker off). The cloud accepts the command and returns 200 even if the device can't execute it. Confirm the switch is online in the mobile app.
Brightness returns to a previous value after onExpected. The cloud restores the last non-zero brightness when transitioning OFF → ON. Send set_level explicitly if you want a different value.
  • tplink_kasa.js — TP-Link Kasa smart switches / plugs (local + cloud).
  • wled.js — LED strip controllers (local HTTP).
  • philips_hue.js — Hue bridge (local REST + CLIP v2).
  • insteon_hub.js — Insteon Hub legacy HTTP.
  • lutron_caseta.js — Lutron Caseta on the Smart Bridge (LEAP).