Version: 1.9.0 Author: CliveS Platform: Indigo 2022.1 or later / macOS / Python 3.10+
Developed and tested on Indigo 2025.2 / Python 3.13. Older Indigo releases that meet the minimum API version above should also work — the API floor is what Indigo's plugin loader actually checks.
Plugin ID: com.clives.indigoplugin.deviceactivitymonitor
GitHub: https://github.com/Highsteads/DeviceActivityMonitor
v1.9.0 renamed this plugin from Sensor Monitor → Device Activity Monitor. Same lineage; the new name better describes both halves of what it now does: per-device change logging and group-change custom triggers.
- What it does
- Installation
- Credentials —
IndigoSecrets.pyvsIndigoSecrets_example.py - Quick start in 5 minutes
- Use cases
- Multi-Sensor Trigger — deep dive
- Configuration file
- Plugin menu
- Discovery
- Log output examples
- Group device states
- Repository structure
- Migrating from "Sensor Monitor"
- Changelog
- License
Device Activity Monitor watches Indigo devices and variables and reacts to their state changes two complementary ways:
- Logs changes to the Indigo event log with millisecond-precision timestamps and
custom on/off labels (
Front Door OPEN,Lux Level: 450 -> 520). - Fires custom triggers when any device in a named Group changes state, with
an optional direction filter (
any change/becomes ON/OPEN/becomes OFF/CLOSED).
The two halves are independent and can each be turned on or off at runtime from the
Plugins → Device Activity Monitor menu — use just the change-logger, just the group
triggers, or both. The [HH:MM:SS.mmm] timestamp prefix on the logger's output is also
its own toggle, so you can keep the logging on but rely on Indigo's column timestamp
alone if the inline prefix is noise for you. All three toggles persist across plugin
restarts. See Plugin menu below.
Groups are first-class Indigo devices managed via a rich two-list Add/Remove ConfigUI — no JSON editing required. The plugin replaces both:
- A pile of one-trigger-per-device "Device State Changed" triggers
- The older Group Change Listener plugin (Morris's) — this plugin is based on Group Change Listener and updated to a dual-list Add/Remove ConfigUI
…with one subscription, one config file for logging, and one Indigo Group device per trigger group.
- Download
Device_Activity_Monitor.indigoPlugin.zipfrom the latest release - Unzip and double-click
Device_Activity_Monitor.indigoPlugin— Indigo will prompt to install - Plugins → Manage Plugins → enable Device Activity Monitor
- The plugin auto-creates its config folder at
<install>/Preferences/Plugins/com.clives.indigoplugin.deviceactivitymonitor/ - Open the Indigo Event Log — you should see the startup banner and the loaded device count
This plugin (along with all CliveS Indigo plugins) reads sensitive values from a shared master credentials file at:
/Library/Application Support/Perceptive Automation/IndigoSecrets.py
| File | Purpose | Real data? | Committed to GitHub? |
|---|---|---|---|
IndigoSecrets.py |
Working file the plugin reads at runtime. Keep a backup in a password manager. | YES | NO — listed in .gitignore |
IndigoSecrets_example.py |
Template only — empty placeholders. Shipped in the plugin bundle. | NO | YES |
If you do not have IndigoSecrets.py, copy IndigoSecrets_example.py from
the plugin bundle to that location and fill in your values. Or skip
IndigoSecrets.py entirely and enter values via the plugin's configuration
dialog — IndigoSecrets.py wins over the dialog when both are set.
If a required value is set in NEITHER source the plugin logs an ERROR
pointing the user to either fill in the matching field or add the key to
IndigoSecrets.py.
Note for this plugin specifically: Device Activity Monitor reads no external
APIs and needs no credentials in normal use. The IndigoSecrets_example.py
file is shipped for ecosystem consistency only — there is nothing to fill in
unless you extend the plugin yourself.
Goal: get a Pushover notification when any of three motion sensors in the living room detects movement.
-
Create the group device
- Devices → New Device
- Type = "Device Activity Monitor → Device Activity Monitor Group"
- Name it "Living Room Presence"
- Show devices from: pick
Living Room(or(All folders)) - In Available devices: ⌘-click your 3 motion sensors
- Click Add to Group ↓
- Save
-
Create the trigger
- New Trigger → Type = "Device Activity Monitor: Group Changed"
- Group: pick "Living Room Presence (3 members)"
- Fire on: "Any device becomes ON / OPEN / detected"
- Actions tab: add your Pushover action
-
Done. Wave at any of the three sensors — Pushover fires. The trigger does NOT re-fire while the sensor stays active, and does NOT fire on the off transition (because of the direction filter).
That's the headline workflow. The rest of this README covers the variants.
You want a single log line every time a sensor changes state, so the event log becomes a usable timeline. No triggers, no actions — just clean logging.
- Plugins → Device Activity Monitor → Discover All Devices
- Open
device_activity_monitor_config.jsonand uncomment / add the lines you want logged - Plugins → Device Activity Monitor → Reload Config File
Each configured state gets a log line of the form:
[14:23:01.452] Front Door Contact OPEN
[14:25:33.104] Hall PIR Occupancy ON
Indigo has a built-in "Device State Changed" trigger for this, but if you'd rather pick by a friendly name from a list of Group devices:
- Create a group with one member (the device)
- Trigger → Fire on = "Any device becomes ON / OPEN / detected" (or the OFF variant)
Useful when you want a consistent "group" mental model across single and multi-device cases.
Fire whenever any member of the group changes any state. Equivalent to Morris's Group Change Listener with no filter.
- Group: any number of devices
- Trigger → Fire on = "Any change (default)"
Use this for log-spam-style "something happened in this area" reactions where direction doesn't matter.
Fire once when the first member transitions from off to on. Doesn't re-fire while any other member is also active.
- Group: 3 presence sensors in a room
- Trigger → Fire on = "Any device becomes ON / OPEN / detected"
Common uses:
- Turn on the lights when anyone enters a multi-sensor room
- Pushover "movement in the garage" without 3 separate triggers
- Start a "room occupied" timer
Fire only on the off transition.
- Group: same room sensors
- Trigger → Fire on = "Any device becomes OFF / CLOSED / clear"
Common uses:
- Start a delay timer to turn lights off when the last sensor clears
- Trigger an "armed away" routine when a contact closes
Pairing: use one group with two triggers — one direction-filtered to "becomes ON" and one to "becomes OFF". Each fires only on the relevant edge.
If multiple devices in a group could be the source, capture the firing device's name to an Indigo variable so your actions can use it:
- Trigger ConfigUI → tick Save firing device
- Pick a variable
- Save value: "Device Name" or "Device ID"
- In your action, reference the variable with
%%v:NN%%or read it in a Python action script
Examples:
- Pushover body: "Movement detected by %%v:firing_sensor%%"
- Script: branch based on which front-of-house contact was triggered
The trigger doesn't have to fire blindly. Indigo's standard Conditions and Actions tabs are fully available:
- Conditions tab: "only fire between sunset and sunrise" / "only when the alarm is armed" / "only when nobody is home"
- Actions tab: chain to action groups, Python scripts, action collections, send-to-variables, control pages, etc.
For complex multi-room logic, fire one trigger from each group and have the target action group read all the various states it cares about.
Add Indigo variables to the variables[] section of
device_activity_monitor_config.json:
"variables": [
{"id": 241032502, "name": "Lux_Level", "label": "Lux Level"}
]
Output:
[14:26:10.512] Lux Level: 450 -> 520
The headline feature. This section walks through every option in detail.
Devices → New Device → Type = "Device Activity Monitor → Device Activity Monitor Group".
You get a dialog with these fields:
| Field | What it does |
|---|---|
| Show devices from | Folder filter for the Available list. (All folders) shows everything; (Root) shows un-foldered devices; named folders narrow to that folder's contents |
| Available devices | Multi-select list of devices NOT yet in this group. Cmd-click to select multiple, then click Add. Refreshes as the folder filter changes |
| Add to Group ↓ | Moves the Available-list selection into the Members list |
| Current group members | Multi-select list of devices currently in the group. Devices the bridge has since deleted show as <missing device id NN> so you can clean them up |
| ↑ Remove from Group | Moves the Members-list selection back to "available" |
To edit a group later, double-click the Group device — same dialog, same flow. There is no JSON editing.
To delete a group, right-click → Delete (standard Indigo flow). Any triggers wired to the deleted group will log a warning on next plugin reload that they reference a now-missing group.
Each Group device shows N members as its display state in the Indigo device
list.
New Trigger → Type = "Device Activity Monitor: Group Changed".
| Field | What it does |
|---|---|
| Group | Dropdown of all Group devices, each labelled with its name and live member count. This is Indigo's native device picker filtered to self.damGroup — folder tree, search, the lot |
| Fire on | Direction filter (see next section) |
| Save firing device | Tick to capture which member triggered the event into an Indigo variable |
| Save to variable | (only when Save firing device is ticked) target variable |
| Save value | (only when Save firing device is ticked) "Device Name" or "Device ID" |
The Fire on menu has three options. They look at the device's onState
attribute before and after the change.
| Option | Fires when… |
|---|---|
| Any change (default) | Any state on a group member changes — including non-onState states like temperature readings or battery level. Use sparingly with chatty sensors |
| Any device becomes ON / OPEN / detected | A member's onState flips from False to True. Edge-triggered: doesn't re-fire while it stays on. Use for occupancy, door-opens, alarms |
| Any device becomes OFF / CLOSED / clear | A member's onState flips from True to False. Edge-triggered: doesn't re-fire while it stays off. Use for "last sensor cleared" timers, door closes |
The directional options only fire on onState transitions, so they ignore
chatty value updates (temperature, illuminance, etc.) entirely.
Standard Indigo Actions tab. The trigger fires the same way as any built-in Indigo trigger — Pushover, action groups, scripts, control pages all work identically.
If you ticked Save firing device, your variable now contains the name or
ID of the device that triggered the firing — reference it via %%v:NN%% in
text fields, or read it directly in Python script actions.
Whenever a trigger fires for a Group device, the plugin also writes these states on the Group device itself (useful for control pages or other triggers):
memberCount— number of devices in the grouplastFiringDevice— name of the device that triggered the most recent firelastFiringTime—YYYY-MM-DD HH:MM:SSlastFiringDirection—activated/deactivated/changedstatus— display string, e.g. "3 members"
Lives at:
<install>/Preferences/Plugins/com.clives.indigoplugin.deviceactivitymonitor/
├── device_activity_monitor_config.json ← edit this
└── device_discovery.json ← generated by Discover Devices
<install> resolves via indigo.server.getInstallFolderPath() so the path
follows your active Indigo version automatically.
{
"_usage": "Lines starting with # are ignored. Reload plugin after changes.",
"excluded_ids": [],
"devices": [
{"id": 123456789, "name": "Front Door", "state": "onState", "label": "Front Door", "on_text": "OPEN", "off_text": "CLOSED"},
{"id": 987654321, "name": "Basin mmWave","state": "pirDetection", "label": "PIR"},
{"id": 987654321, "name": "Basin mmWave","state": "presence", "label": "mmWave Presence"}
],
"variables": [
{"id": 241032502, "name": "Lux_Level", "label": "Lux Level"}
]
}#at the start of a line disables that entry. Use this to comment out a device without deleting the line- Trailing commas before
]or}are silently cleaned up - Multiple rows with the same
idmonitor multiple states on one device (e.g. PIR and mmWave on a multi-state sensor) - After saving, reload via Plugins → Device Activity Monitor → Reload Config File — no plugin restart required
| Field | Required | Description |
|---|---|---|
id |
Yes | Indigo device or variable ID (integer) |
name |
No | For your reference only — never used by the code |
state |
Yes | "onState" reads device.onState; any other name reads from device.states |
label |
No | Log text shown after the device name (defaults to name) |
on_text |
No | Log text when state is True (default ON) |
off_text |
No | Log text when state is False (default OFF) |
As of v1.8.0, groups live as damGroup Indigo devices, not in the JSON file.
This file is logging-only.
Under Plugins → Device Activity Monitor:
| Item | What it does |
|---|---|
| Discover All Devices (generate config file) | Scans every Indigo device, classifies contact / motion / presence candidates using device type, Zigbee2MQTT capability flags, and name keywords. Writes device_discovery.json (full inventory) and a fresh device_activity_monitor_config.json (sensor candidates active, all others commented out for reference). Preserves excluded_ids across re-runs |
| Find Contact & Motion Sensors | One-shot log dump of all sensor candidates with ready-to-paste config entries — useful for a quick check without regenerating the whole config |
| Reload Config File | Re-reads the JSON and re-validates without a full plugin restart. Use after editing the config file by hand |
| Toggle Device Change Log (on/off) | Flips per-device / per-variable event-log lines on or off. When OFF the plugin still subscribes to changes (group triggers keep working) but writes nothing to the event log |
| Toggle Group Change Triggers (on/off) | Flips group-device-driven custom triggers on or off. When OFF, damGroup device changes no longer fire damGroupChange events even if matching triggers are enabled |
| Toggle Timestamps in Log (on/off) | Strips or restores the [HH:MM:SS.mmm] prefix on every line the plugin writes to the event log. Indigo's own column timestamp is unaffected |
| Show Plugin Info | Prints the startup banner on demand, including current state of the three toggles above |
All three toggles persist across plugin restarts (stored in pluginPrefs as
logEnabled / groupEnabled / timestampEnabled) and default ON.
Discovery uses a layered classifier that gets the right answer even on generic Zigbee2MQTT devices that publish stub fields they don't physically have:
- Z2M ownerProps (authoritative when available):
has_contact,has_occupancy,has_presence,has_pir - deviceTypeId hints:
z2mContactSensor→ contact,z2mOccupancySensor→ motion - Motion-keyword veto: if the device name contains
motion,pir,presence,occupancy,mmwave,radar, it can't be a contact sensor - State-name match:
contact/doorSensor/windowSensor→ contact;occupancy/pirDetection/presence/motion/motionDetected→ motion - Name-keyword match:
contact,door,window, etc.
To permanently exclude a device from discovery output, add its ID to the
excluded_ids array in the config file. Subsequent re-discovery runs will
respect the exclusion and leave that device commented out.
To add a sensor that discovery missed, just add a line manually to
devices[] — discovery never deletes manually-added entries.
[14:23:01.452] Hall PIR Occupancy ON
[14:23:01.891] Basin Sensor mmWave Presence ON
[14:25:33.104] Front Door Contact OPEN
[14:25:41.230] Front Door Contact CLOSED
[14:26:10.512] Lux Level: 450 -> 520
[14:30:00.001] [Device Activity Monitor] Device renamed: 'Test Sensor' -> 'Hall PIR' (ID: 99887766)
[14:31:05.774] [Device Activity Monitor] WARNING - Monitored device deleted: 'Hall PIR' (ID: 99887766)
Each damGroup device exposes:
| State | Type | Updated when |
|---|---|---|
memberCount |
Integer | Group device is saved / reloaded |
status |
String | Display state — e.g. "3 members" |
lastFiringDevice |
String | A trigger wired to this group fires |
lastFiringTime |
String | YYYY-MM-DD HH:MM:SS of last fire |
lastFiringDirection |
String | activated / deactivated / changed |
Useful for control pages ("Living Room presence last activity: 2 minutes ago") or for chaining one group's activity into another trigger's condition.
README.md ← this file (GitHub displays this)
Device_Activity_Monitor.indigoPlugin/
├── Contents/
│ ├── Info.plist
│ └── Server Plugin/
│ ├── plugin.py ← main plugin code + ConfigUI callbacks
│ ├── Devices.xml ← damGroup device type
│ ├── Events.xml ← Group Changed trigger
│ ├── MenuItems.xml ← Plugins menu
│ ├── plugin_utils.py ← startup banner helper
│ ├── discover_devices.py ← standalone Script Editor discovery
│ ├── find_contact_sensors.py ← standalone Script Editor sensor finder
│ ├── test_plugin.py ← 86 tests, runs without Indigo
│ └── IndigoSecrets_example.py ← credential template (unused — ecosystem standard)
If you ever installed a pre-v1.9.0 release of this plugin under the old name:
- Disable Sensor Monitor in Indigo Manage Plugins
- Delete the old bundle at
<install>/Plugins/Sensor_Monitor.indigoPlugin/ - Install Device Activity Monitor v1.9.0
- Move
<install>/Preferences/Plugins/com.clives.indigoplugin.sensormonitor/→<install>/Preferences/Plugins/com.clives.indigoplugin.deviceactivitymonitor/ - Rename
sensor_monitor_config.json→device_activity_monitor_config.jsoninside that folder - Old
smGroupdevices are not auto-migrated todamGroup— re-create groups via Devices → New Device
(For me specifically: that migration was done at the moment of the rename, so this section exists only for completeness if I ever do a clean reinstall.)
| Version | Date | Change |
|---|---|---|
| 1.9.5 | 2026-05-23 | Three runtime toggle menu items — Device Change Log, Group Change Triggers, Timestamps in Log. Each is independent, persists across restarts, defaults ON |
| 1.9.4 | 2026-05-14 | Discovery correctly classifies multi-capability Aqara presence sensors (PS-S04D, FP1 etc.) using pirDetection / presenceDetectionOptions state names |
| 1.9.3 | 2026-05-13 | Added on_value / off_value config keys for explicit value matching on string-typed states (e.g. presenceEvent enter/leave) |
| 1.9.2 | 2026-05-13 | Trust Z2M has_* flags to exclude non-motion devices in discovery |
| 1.9.1 | 2026-05-12 | Minor discovery and config fixes |
| 1.9.0 | 2026-05-12 | Renamed Sensor Monitor → Device Activity Monitor (bundle ID, device type id, event id, config filename all changed; legacy migration code stripped) |
| 1.8.1 | 2026-05-12 | Dropped JSON-groups backward-compat path; damGroup devices the sole source of truth |
| 1.8.0 | 2026-05-12 | Groups are now first-class Indigo devices with Add/Remove ConfigUI |
| 1.7.2 | 2026-05-12 | Direction filter (any/activated/deactivated) on group triggers |
| 1.7.1 | 2026-05-11 | Moved config files from Logs/ to Preferences/ |
| 1.7.0 | 2026-05-11 | Group-change custom triggers (JSON-defined, since superseded by damGroup devices) |
| 1.6.0 | 2026-05-11 | Z2M-aware sensor classifier (uses ownerProps has_* flags) |
| 1.5.9 | 2026-02-27 | Sync live plugin (multiple features since v1.4.0) |
| 1.4.0 | 2026-02-27 | Plugin menu — Discover Devices, Find Contact Sensors, Reload Config File |
| 1.3.0 | 2026-02-27 | JSON config file support |
| 1.2.0 | 2026-02-27 | Variable monitoring |
| 1.1.0 | 2026-02-27 | Startup validation, rename detection, deletion warning |
| 1.0.0 | 2026-02-27 | Initial release |
GPL-3.0 — see plugin source files for details.