Home Assistant custom component for Universal Devices eisy controllers running IoX 6.0+.
If you've used HA's built-in isy994 for years, the things this integration adds on top:
- IoX programs are first-class HA devices. Each program (and program folder) becomes its own device with
Run/Run Then/Run Else/Stop/Enable/Disablebuttons, an enable switch, a Run at startup toggle, andLast Run/Last Finished/Next Scheduled Runtimestamp sensors. Triggering and observing IoX programs no longer requires a status program + actions program pair plus YAML. - Native event entities for keypad / scene buttons. KeypadLinc accessory buttons, RemoteLincs, and any nodedef with a
cmds.sendslist becomeevent.*entities exposed through HA's device-trigger UI — noudi_iox_controlbus events, no listening for raw control codes. Fires on every press, including same-type repeats. Replaces the legacysensor.<keypad>_<letter>integer-state hack. - Scene members in the more-info dialog. Each IoX scene (group) publishes its member nodes through HA's group framework, so opening a scene lists its member lights / switches / fans inline (mixed domains, same UX as a HA light group). Core
isy994exposes scenes as bare switches with no member visibility. (Hidden member entities are omitted by HA's frontend, as with any group.) - Suggested area from your IoX folder layout. Organize nodes or programs into folders on the controller (
Kitchen,Garage, …) and HA pre-fills each device's Suggested Area from the immediate parent folder. - Dynamic device support. Core
isy994carries several hardcoded type-prefix tables to decide which HA platform each Insteon device belongs on — adding a new device class meant editing the integration. This integration reads each device's nodedef from the controller (the wire shape: accepts / sends / properties / editors). Insteon devices land on the same baseline platforms as core, but Z-Wave, Zigbee, Matter, and any PG3 node-server plugin all classify themselves automatically with no integration update needed. - Z-Wave / Insteon configurables as native HA entities. On Level, Ramp Rate, Backlight, ramp/scene values, Z-Wave configuration parameters — anything the controller publishes as an editor surfaces as a
number/select/switchentity (no service calls, no YAML). - WebSocket-only event stream + Repair-card lifecycle UX. State updates ride a single WebSocket; entity availability follows WS health. New / removed / renamed devices on the controller surface as Repair cards prompting a reload instead of being silently missed.
Side-by-side notes: the two integrations register distinct domains (
isy994vs.udi_iox) and coexist on the same HA instance — useful for ISY-994 + eisy mixed setups. Seedocs/coexistence-with-core.mdfor a per-platform comparison.
If you have ISY-994 hardware, use the existing Home Assistant core isy994 integration (which stays on pyisy 3.x). The two integrations register distinct domains and coexist on the same HA instance.
The earlier-generation Polisy controller is end-of-life and no longer a target for this integration — the SSDP / DHCP discovery matchers have been narrowed to eisy. Polisy users can still configure the integration manually (the IoX 6 protocol is the same), but auto-discovery won't surface a Polisy on the network.
hacs-isy994 was a stable beta-testing channel for the upstream isy994 integration which served its purpose, but I no longer want to maintain legacy hardware support for Home Assistant's core isy994 and try to maintain both new features and legacy support in another repo. The eisy / IoX-6+ rewrite is a clean break: different library (pyisyox v6 with JWT/portal auth, WebSocket-only, classifier-driven entity routing, ergonomic Node wrappers). Forcing existing hacs-isy994 users onto that rewrite would regress their working ISY-994 setups, so this is a new repo with a new domain.
IF YOU ARE USING THE CORE ISY994 INTEGRATION: While they can co-exist side-by-side, you may wish to remove that integration before adding this one. For the large portion of entities that overlap, they will try to get the same
entity_idand you will end up with everything suffixed withdomain.*_2. To keep your dashboards intact as much as possible, remove theisy994integration, restart HA once (needed for installing this repo anyways) and then add this integration to restore the entities. Anyentity_idthat you manually renamed on the old integration will need to be renamed again.IF YOU HAVE THE
hacs-isy994HACS CUSTOM COMPONENT INSTALLED (not the same as HA's built-inisy994integration above): remove it first — it's now archived. Unlike the built-inisy994integration,hacs-isy994pins an oldpyisyoxrelease that's incompatible with this integration's, and HA installs Python dependencies into one shared environment shared by every integration. With both installed, whichever loads last on a restart silently re-pinspyisyoxout from under the other, and this integration's config flow fails withInvalid handler specified(log showscannot import name 'Controller' from 'pyisyox'). Delete thehacs-isy994config entry and itscustom_components/isy994folder, restart HA, then install this integration.
Before starting the setup flow, gather:
-
A Universal Devices portal account (the same email + password you use to sign in at https://my.isy.io). The eisy on IoX 6+ authenticates against the portal — local admin credentials are not supported by the integration yet.
- If you don't have a portal account, follow the "Sign up" link on https://my.isy.io and link your controller there first.
-
The base URL of your eisy, including the scheme and port. Examples:
https://eisy.local:443(default portal-mode port on the local network)https://192.168.1.50:443(by IP)https://eisy.example.com:443(with a non-default hostname)
The default port for portal (JWT) auth is
443. Visithttps://<your-controller>/descin a browser to confirm the controller responds — if you get a self-signed-cert warning, that's normal.
This is a HACS Custom Repository:
- Open HACS in Home Assistant:
(or HACS → ⋮ → Custom repositories → add
https://github.com/shbatm/hacs-udi-iox, category Integration). - Install, restart HA.
- Add the integration:
(or Settings → Devices & Services → + Add Integration → search "Universal Devices IoX").
The initial setup flow asks for the following. HA also recognises eisy controllers via SSDP and DHCP discovery — if your controller is on the same network as HA, the integration usually surfaces it automatically and prefills the host field; you only need to enter credentials.
| Parameter | Required | Description |
|---|---|---|
URL (host) |
yes | The full controller URL, e.g. https://eisy.local:443. Use the same URL you'd open in a browser. |
Universal Devices portal email (username) |
yes | The email you registered at https://my.isy.io. |
Universal Devices portal password (password) |
yes | The password for that portal account. |
Verify SSL certificate (verify_ssl) |
no (default: off) | Leave off unless you've installed a CA-trusted certificate on the controller — eisys ship with a self-signed cert that will fail verification. |
After the credentials step the flow lands on the Options step (the same form is later available via the integration card's Configure button — see Configuration below).
- Settings → Devices & Services → Universal Devices IoX → ⋮ → Delete (removes the config entry, its devices, and its entities).
- Optionally remove the repository in HACS (Integrations → Universal Devices IoX → ⋮ → Remove) and restart HA.
All of these are revisitable at any time via Settings → Devices & Services → Universal Devices IoX → Configure. Changing them triggers a reload, so the new values take effect immediately.
| Option | Default | Description |
|---|---|---|
Node Sensor String (sensor_string) |
{SENSOR} |
A node whose IoX name contains this exact string is forced onto the (binary) sensor platform instead of its classifier-assigned one (e.g. a generic Insteon I/O Linc input). The marker is matched verbatim and stripped from the HA device/entity name (so Garbage Disposal {SENSOR} → Garbage Disposal). Use a distinctive bracketed marker — the bracketed default avoids matching node-server entities that legitimately contain the word "sensor". |
Ignore String (ignore_string) |
{IGNORE ME} |
Any device, program, or folder whose IoX name contains this substring is skipped entirely — no entity/device is created. Add the substring to a node's, program's, or folder's name in the eisy admin UI to hide it (and everything under a folder) from HA without deleting it from the controller. |
Restore Light Brightness (restore_light_state) |
off | When on, turning a light on from HA restores the previous brightness instead of using the device's built-in On Level (Insteon OL). When off, the device decides the start level — usually the right call for Insteon switches with a configured On Level. |
Enable adding variables entities (enable_variables) |
on | When off, IoX variables (Integer + State) are not exposed as number entities. Turn off if you have many variables and only use them inside IoX programs (faster startup). |
Enable adding program entities (enable_programs) |
on | When off, IoX programs are not exposed as switches / covers / locks / etc. Turn off if you don't drive IoX programs from HA. |
Enable adding network resource entities (enable_networking) |
off | When on, IoX network resources (HTTP / TCP / UDP triggers configured under Network → Network Resources in the admin UI) are surfaced as buttons. |
If you change your Universal Devices portal password the integration will detect the failed login on the next refresh and surface a Repair card prompting you to reauthenticate. The reauth flow asks for the username + password only — the host stays as-is.
Insteon KeypadLinc accessory buttons (and any nodedef with a cmds.sends
list — most Insteon load/dimmer controls and many PG3 plugin sources)
are exposed as event entities. Each press updates the entity's state
timestamp and sets its event_type attribute (on, off, fast_on,
fast_off, fade_up, fade_down, fade_stop, …).
Use a device trigger for "fire on every press." Settings → Automations & Scenes → Create Automation → When → Add trigger → Device, pick the KeypadLinc / SwitchLinc / etc., and choose e.g. "… was switched On". This fires every time the button is pressed, even when the previous press was the same type — which is the behaviour most Insteon users expect.
Equivalent YAML:
trigger:
- platform: device
domain: udi_iox
device_id: <copy from the UI>
entity_id: event.hallway_keypad_b
type: "on"
action:
- service: light.toggle
target:
entity_id: light.kitchen_mainWhy not a state trigger? A state trigger configured with attribute: event_type and to: "on" only fires when the attribute changes: pressing
the same button twice in a row is the same event_type value both times,
so the second press is silently dropped. The device trigger above sidesteps
this by matching against the new state's event_type on every update,
not just transitions.
The integration surfaces every device on your controller — Insteon, Z-Wave, Zigbee, Matter, and any PG3 node-server plugin you have installed. HA platform routing is driven by pyisyox's classifier reading each device's nodedef, not a hard-coded table.
If you bridge a device through a PG3 plugin and Home Assistant has a first-party integration for that same device (Sonos, Hue, Rachio, Roku, Plex, Ecobee, Shelly, WLED, etc.), install HA's native integration directly. It will almost always be the better source of truth — local-push state, manufacturer-aware quirks, more device-specific features. The two coexist fine: this integration still exposes the device's IoX-side state (useful if your IoX programs reference the device), and the native integration handles the device itself.
See docs/supported-devices.md for the full list of PG3 plugins with HA-native equivalents, the cases where the IoX path is the right call, and roadmap notes for the platforms still to come.
Bug reports go through GitHub Issues — the Bug report template walks you through the diagnostics + debug-log attachments most issues need. Two things to grab before you file:
Captures a redacted snapshot of what the integration sees: nodes, programs, variables, WebSocket health, and the entry's options. It's the fastest way to confirm the wire shape of whatever's misbehaving. (HA docs on diagnostics)
-
Click on the hub device (e.g. IoX-Testing, your eisy's name).
-
On the hub device page, click ⋮ in the Device info card → Download diagnostics.
-
Attach the
.jsonfile to the issue.
PII (passwords, portal email, host) is redacted; node addresses and names stay verbatim so triage keeps context.
Required for any state / event / command bug — the integration emits per-event lines at DEBUG so we can see which control / property a bad reading came in on. (HA docs on enabling debug logging)
-
Click ⋮ in the top-right corner of the integration page → Enable debug logging.
-
Reproduce the bug — toggle the broken switch, fire the program, etc.
-
Click ⋮ → Disable debug logging — HA automatically downloads the captured log file.
-
Attach the file to the issue.
Debug logging captures every WebSocket frame plus REST request, so keep the reproduction window short — even a few minutes generates several MB.
Open issues and milestones are tracked in GitHub Issues.

