changes in V2022.41.383@ 2023-04-18
==================
1. fixed error for unifi protect event thumbnails
in some cisrcumstance the thumbnail is not available immedeately after event.
the plugin now tests if the thumbnail image is ready, then they are copied. this might take up to 10 secs.
in addition a "snapshot" is taken immediately. they can be requested at any time
2. removed code for old deprecated nvr video system. The plugin now only supports PROTECT
3. made config a little more readable
changes in V2022.42.384@ 2023-05-07
==================
1. added menu item to clean up ~/.ssh/know_hostsfile
if you have issues with ssh loging in to your unifi devices, you can now reset the entries in ~/.ssh/konwn_hosts file
2. added config option in userid/password section to ignore known_hosts
ie use ssh -o StrictHostKeyChecking=no [email protected]
instead of ssh [email protected]
changes in V2022.42.387@ 2023-07-20
==================
1. added config option in general section to set request - curl timeout to any value >=1 sec
values < 3 secs will likely create timeouts.
2. made error logging more verbose
changes in V2022.42.395@ 2023-12-06
==================
- added check for protect camera: is properly defined?
line 8953, in getProtectIntoIndigo cameraId = dev.states["id"] KeyError: 'key id not found in dict'
- fixed mismatch for detecting dict start end while listening to AP SW etc.
changes in V2022.42.396@ 2023-12-15
==================
- added option to select a different way to set POE on/off switches. Some newer switch devices only support a unix command swctrl
all of my older switches also support unix swctrl command. The old way used a CLI command.
changes in V2022.43.397@ 2024-01-13
==================
- in case of http error, the plugin does not print a large dump anymore, but it will
= check if timeout, retry 2 more times, if sucessful, a warning will occur .. fixed after xx tries
= if not sucessfult it will try 30 more times (after some minutes) and if not sucesfull the plugin will reload eg when the unifi device is down, or you load a new software
hanges in V2022.47.425@ 2024-10-26
==================
- added devices for system controller an system protect (they can be the same but will show as 2)
the relevant properties (like in config) can be set here
important dev states are preseneted: disk space, memory utilization ..
- added option for different userid/password and port for protect vs controller device
if not specified it is set as same
you can set them in config aand device edit for controller and proetct system devices
- added error catch for bad read of disk space from unifi conroller / protect systemetc
changes in V2022.46.422@ 2024-10-20
==================
- adding option to have protect recording and controller on 2 different devices
in config in camera section set protect ip and port number, if not set they will inherit the controller ip and port number and hence be on the same box
they must be on the same os level and settings:
same user id, same password. protect is fixed to be on port 433 and hence controller also ahs to be on port 433.. at least for the time being.
- added action to set zoom, position (only G5+) brightness, saturation, sharpness, hue, pan tilt,...
- added general action for camera. can do any action in script: zoom, set to predefined positions, set contrast, led on off, speak on off ,...
with this you can eg drive zoom with indigo variables or dev states in a indigo server action script
see examples
- changed protecEventSleepTime from 0.5 secs to 500mSec in puglinconfig. as 0.5 is not propery show as 0.5 but 0.4999999 and hece not displayed in config
- removed obsolete NVR type video functions (predecessor to protect)
changes in V2022.44.405@ 2024-10-10
==================
- added error logging for camera section
- added check for name is none in cameras, added option in config to create / not create these devices
- added state "created" for camera devices
- added 3 secs for time window for event detetction
- added menu item to zoom/ move camera, experimental
changes in V2022.43.399@ 2024-01-22
==================
- if not logged in due to eg expiration, better logging
- suppress "fixed after xx" try for protect thumbnail http-gets. this can take longer depending on how long an event takes
changes in V2022.48.2022.49.431@ 2026-03-21
==================
- added update states for mini/flex switches through HTTP query to controller
- fixed rx tx rates for port states
- added power on off on switches through controller. (these changes take >10 secs to take effect as the controller does an "adopt" function)
The Flex and mini switches do not support ssh commands, hence there was no support for that function before
In config "".. use controler" has to be set to "ON"
changes in V2026.50.436 @ 2026-06-10
==================
1. structured config page significantly, much clearer
2. option to just use the gateway / UDx for events, w/o ssh listening process for each AP / switch
this works fine, just has a time lag of 1-10 secs as compared to directly listening to the AP events
(needs to go to the controller and then to the plugin.)
not important for going offline for a device (client), but ie coming home will be a recognized a little bit delayed.
3. ===>>> added support for new unifi sensors <<<<======
### UniFi Protect Sensors (USL family)
**All-In-One Sensor (UP-Sense)** — motion, contact, temperature, humidity, ambient light (`light` state), battery, `sensorButtonPressedAt` / `sensorButtonPressedAt_Last` (datetime of last/previous function-button press). Water leak: `waterDetected`, `waterDetectedAt`, `waterDetectedAt_Last`, `waterDetectedEnabled`. When leak detection is enabled (exclusive mode on this sensor) the device behaves like a water sensor: onOffState on = wet, display shows dry/LEAK!
- **Entry Sensor (USL-Entry)** — door/window contact open/closed state, battery
- **Motion Sensor (USL-Motion)** — motion detected state, battery
- **Environmental Sensor (USL-Environmental)** — temperature, humidity, battery, `sensorButtonPressedAt`. Water leak: `waterDetected` + timestamps (combined — Protect does not report which probe fired) and per-probe enable flags `waterDetectedInternalEnabled` (built-in contacts) / `waterDetectedExternalEnabled` (wired probe)
- **Glass Break Sensor (USL-Glassbreak)** — glass break detection (`glassBreakAt` / `glassBreakAt_Last`), battery, `sensorButtonPressedAt`
not tested yet:
- **Remote Key Fob (USL-Fob)** — button press events, battery
-** and more to come
### Protect Relay (USL-Relay)
The relay creates four Indigo devices:
- **Protect Relay (relay_protect)** — parent device for output 1; states include `onOffState`, `lastCommand`, `status`, `isConnected`, `MAC`, `id`, `firmwareVersion`, `btSignal`, `lastSeen`, `created`
- **Protect Relay Output 2 (relay_protect_output2)** — independent on/off control for output 2; states include `onOffState`, `lastCommand`, `MAC`, `created` (only the parent device carries `status`)
- **Protect Relay Input 1 (relay_protect_input1)** — dry-contact input 1; states include `onOffState`, `pressType`, `onAt`, `onAt_Last`, `MAC`, `created`
- **Protect Relay Input 2 (relay_protect_input2)** — dry-contact input 2; same states as input 1
changes in V2026.54.443 @ 2026-07-11
==================
NEW:
- prepared for a future CO-only Protect sensor: plausible model identifiers (UP-CO/USL-CO variants, CARBON-MONOXIDE) map to the existing Smoke/CO device type, plus a token-safe "CO" fallback for unknown spellings (matches USL-CO-EU but can never mis-grab CONTACT). The smoke_co type already carries all CO states/display - once the hardware exists, only its status-block name may need adapting
- new device type "Protect FloodLight (UP FloodLight)" (light_protect, Protect modelKey "light"): an Indigo DIMMER: the 6 led levels map proportionally onto the percent scale (level*100/6: 17/33/50/67/83/100%, 0=off; any percent set rounds to the nearest level; Brighten/Dim steps one level). onOffState follows isLightOn (Indigo on/off sends lightOnSettings.isLedForceOn via the internal protect api PATCH /proxy/protect/api/lights/<id>, same as brightness), display "ON/off + timestamp"; states: motionDetected + lastMotionAt/_Last (PIR), isDark, ledLevel (1..6), lightMode (off/motion/always), pirSensitivity/pirDuration/luxSensitivity, isLightForceOn, isConnected/status, firmware/ip/lastSeen. Created at bootstrap (gated by protectCreateSensors), updated live via WebSocket; a newly adopted floodlight schedules a protect refresh automatically. New action "FLOODLIGHT Protect set light ON / off" and menu item "FLOODLIGHT set light ON / off.." (same as the device on/off command: forces the light on / releases it to its configured mode; both can also set the brightness, ledLevel 1..6, via the internal protect api - on/off and brightness independently selectable)
- every newly created Indigo device is now logged (info level): created new device "<name>" type:<deviceTypeId> address:<MAC> - covers all 27 creation paths (auto-detect, Protect sensors/cameras/relays/speakers, clients, ...) via one shared wrapper, independent of debug settings
- QUICK SETUP section in plugin config: pick what your controller device has built in from one menu (hosted Mac/rPi | CloudKey | controller w. built-in switch | +AP | UDM-Pro/UCG gateway+switch | UDM gateway+switch+AP), enter only the needed parameters (controller IP, web login, ssh login, UDM unix login, Protect IP/login/API key), optional Protect block (enabled / same machine / with cameras / with sensors), then one button fills all the regular config sections below - nothing is saved until SAVE is pressed, so everything can be reviewed first. External switches/APs are found by auto-detect and are never changed by the wizard; the controller's built-in switch/AP use the reserved slots SW#13/AP#20 (filled automatically). The wizard resets on every dialog open and prefills its entry fields from the current settings, so nothing has to be retyped; the result line reports how many parameters actually changed (details in the log)
- new prefs protectCreateCameras / protectCreateSensors (both default true, existing installs unchanged): gate the creation of NEW Protect camera resp. sensor/relay/speaker/SuperLink devices - existing devices keep updating either way; set by the quick setup "with cameras" / "with sensors" checkboxes
- Protect on/off sensors now show a change-stamped timestamp in displayStatus: "<state> YY-MM-DD HH:MM:SS" (open/closed, MOTION/clear, GLASS BREAK!/ok, ACTIVE/idle, SMOKE/CO, LEAK!/dry, connected/disconnected). The time re-stamps only when the state word actually changes (not every poll)
- same change-stamped timestamp added to: analog sensor displays (temperature/humidity, AQI/vape/CO2), the Protect speaker (idle/playing/vol%), the SuperLink gateway (lower-case connected/disconnected + onOffState from isConnected), relay inputs (in1:ON/off) and relay outputs (on/off in the onOffState display)
- displayStatus padding is now measured, not guessed: when PyObjC/AppKit is available the plugin measures each status word's real pixel width in the 13pt system font and pads with spaces to a common ~80px target (one formula for every word, incl. keyfob/speaker/relay strings that the hand-tuned table never covered; results land within +-2px). Without PyObjC it falls back to the per-word table as before
- the displayStatus shown in the State column uses a 2-digit year (YY-MM-DD, space restrictions); all timestamp STATES (lastStatusChange, lastCommand, keyfob press, leak/glassbreak/relay-input times, created/upSince) keep the full 4-digit year - the year is shortened only at display-render time. Status words are padded per word (padDisplay) so the date-times roughly line up in the State column; at startup stored displayStatus values are reformatted once (and interim 2-digit state values expanded back to 20xx)
- plugin config dialog opens much faster: the dynamic refresh callback (refreshCallbackMethod -> setfilterunifiCloudKeyListOfSiteNames), which the Indigo client fired on every field change/initialisation with a full client->plugin round-trip each time, is removed entirely. The 8 combined-condition visibility flags (showUidUDM, showCtrlActive, ...) are replaced by direct client-side bindings to their section checkbox - no plugin round-trips at all while the dialog is open; everything else happens once at dialog open. Cosmetic trade-off: a few conditional fields (e.g. UDM unix login, Protect login) now simply show whenever their section is open, instead of also depending on controller type / camera system
- removed the curl transport option entirely (requestOrcurl / curl path config options are gone): all controller/Protect http access now uses python requests only - curl was a fallback for OS X El Capitan-era SSL and hasn't been needed for years. The OS/port probe now uses a requests HEAD call as well
- sensor/relay state bridgeId renamed to bridge_used and now holds the SuperLink bridge's Indigo device NAME instead of the raw Protect id; updates live via WebSocket when the sensor switches bridges. The Bluetooth all-in-one UP-Sense (no LoRa bridge) has no bridge_used / bridgeCandidate states - only its btSignal* states
- keyfob: new device-edit option "Button display": Auto (default - follows the fob's buttonLabels field from Protect: securityActions=names, positionHint=numbers, i.e. the "Button Labels" selection in the Protect UI, tracked live via a new buttonLabels state) | names | numbers. Numbers mapping: 1=arm, 2=night, 3=disarm, 4=panic. The buttonPressed state ALWAYS shows "number/name" (e.g. 2/night) regardless of the option. Saving the device dialog re-renders the display immediately (The Protect UI "Button Labels" setting is cosmetic only - the API always sends the internal names)
- more sensor info states: mountType + openStatusChangedAt/_Last (entry, allInOne), tamperingDetectedAt/_Last (siren, smoke/CO), temperature/humidity alarm thresholds (environmental, allInOne), ledEnabled + firmwareUpdateState (all LoRa devices) - filled at bootstrap and updated live via WebSocket
- new states bridgeCandidate1/2/3 on the LoRa Protect devices (entry, motion, environmental, glassbreak, keyfob, siren, smoke/CO, relay - NOT the Bluetooth all-in-one UP-Sense): which SuperLink bridges the device can currently hear, best signal first, as "<signalQuality> <name> <age>" (from wirelessConnectionState.bridgeCandidates; resolved to the Indigo bridge device name when known, filled at bootstrap and live via WebSocket, unused slots empty)
- raw Protect WebSocket events are no longer ALWAYS appended to EVENTS-protectWS.json: new debug checkbox "(17) append raw Protect WS events ..." (default off, like the existing controller-WS one) - saves constant disk writes on normal operation
- debug options reorganized into labeled groups by data source: GENERAL | CLIENT presence tracking | SSH to unifi devices | CONTROLLER http/WS | PROTECT | WRITE FILES to prefs dir | DEVELOPMENT - checkbox ids unchanged, saved prefs unaffected. New checkbox for the BroadCast debug (existed in code but could never be enabled); removed the dead debugVideo pref
- removed the FINGSCAN integration entirely (config options, Fing debug area, sendUpdatetoFingscanNOW, the "fing" flag on status updates)
MAJOR fix:
- all SSH-read switch/AP/gateway dicts were being silently dropped: loads_resilient returns a (obj, dropped_count) tuple, but the caller stored the whole tuple as the dict; every downstream handler then rejected it ("mac" in (dict, 0) is False), so no SSH-read switch, AP or gateway (nor their self UniFi clients) had updated in ~13 days. Only the controller-fed mini switch, which doesn't go through this parse path, still worked. Now the tuple is unpacked
fixes:
- adding a NEW relay no longer floods the log with "WS UNHANDLED modelKey:relay" during adoption: an unknown relay id now schedules a protect bootstrap refresh (so the Indigo devices get created once adoption completes) with a rate-limited info line instead
- status changes on devices without a lastStatusChange state (e.g. the Protect relay) no longer produce the Indigo error "state key lastStatusChange not defined"; also fixed a typo ("fistSeen") that prevented the firstSeen state from ever being seeded by the status path
- keyfob: the FIRST button press on a sleeping (disconnected/away) fob looked lost - the event actually arrives, but the display rebuild still saw isConnected=false (the awayState:ONLINE update only follows after the press event) and rendered DISCONN instead of the press. A button press now asserts isConnected/status=CONNECTED before the display is rebuilt, so the first wake-press shows immediately
- the http target IP and port are now LOCAL variables handed through the call chain (executeCMDOnController -> getunifiOSAndPort) instead of the shared useIPForhttpCmd/usePortforhttpCmd instance variables - eliminates any remaining cross-thread contamination (e.g. controller vs Protect commands racing each other on setups where they use different IPs/ports). Also fixed on the way: the port scan referenced an undefined useTimeout (latent NameError, now a parameter), the Protect branch no longer overwrites the controller port (only visible when controller and Protect ports differ), and a freshly discovered controller port is now used for the login in the same pass instead of the pre-discovery value
- controller logins were intermittently sent to switch IPs (LOGIN failed ... url:https://<switch-ip>:443/api/auth/login): the switch-dict processing thread wrote the shared useIPForhttpCmd variable (its scratch copy of the switch IP) while executeCMDOnController in another thread was between setting the controller IP and issuing the login. The dict functions now use their local ipNumber parameter; only executeCMDOnController sets the URL host (controller or Protect IP). Latent for weeks - it only surfaced once the loads_resilient fix revived the SSH dict processing
- relay outputs/inputs from the Protect WebSocket were mapped by a nonexistent "index" field (always defaulting to 0), so output 2 never updated and the parent showed the wrong output's state; the payload identifies them by "id" (0/1), which is now used (with index fallback)
- switch self-clients read over SSH stayed expired while only the mini-switch (controller-fed) worked: SSH switch dumps often carry no timestamp, so the _doSWdictSELF dedup skipped every read after the first and the self-client was marked up only once. The self-client is now marked up on every successful SSH read (before the dedup); the controller/mini path still respects the dedup so a lingering snapshot of an offline device doesn't hold its client up
- switch/AP self UniFi clients: setStatusUpForSelfUnifiDev now matches case-insensitively (MACs are stored mixed-case) and is also called where the read is confirmed fresh (it was only reached in a branch that is skipped for new/unmatched devices); if no client matches but one exists whose MAC differs only in the last octet, a log line reports it instead of silently leaving the client expired
- auto-detect: an auto-created switch/AP was created but never enabled/polled (stayed blank until manual restart) because the "reboot" flag was cleared right after creation; the plugin now restarts as intended
- auto-detect: switches/APs plugged in after plugin start are now discovered on the periodic scan (previously, with no mini-switch present, discovery only ran at startup)
- auto-detect: a slot is claimed only after the Indigo device is actually created; deferred creation (e.g. no port_table yet) leaves the slot free for a clean retry
- auto-detect: deleting a switch/AP Indigo device now recreates it (occupied-slot path rebuilds the missing device); a guard prevents duplication into a lower free slot
- auto-detect: an actual (re)creation always triggers the restart that enables and polls it - the createUnifiDevicesCounter cap (anti-thrash for the gateway branch) can no longer swallow a genuine creation
- switch port-count snapping in _doSWdictSELF was missing its break, so any switch with an unlisted port count ran up to 52 ports instead of the next-highest supported size
- port command on controller wrote the updated device dict to the wrong list index (off by one, could also raise IndexError on the last device)
changes in V2026.52.441 @ 2026-06-30
==================
- new switch/AP auto-detect: optional setting (off by default) per section in plugin config to automatically discover and create Device-SW-XX / Device-AP Indigo devices straight from the controller's device list, instead of requiring manual device creation; matching/updating already-configured devices is unaffected by this setting either way
- UDM-reserved switch/AP slot relocated from the middle of the slot list (AP-4, SW-12) to new dedicated tail slots (AP-20, SW-13); existing installs are migrated automatically on first run after update (old slot data moved, old slot freed for normal use, logged)
- devices are now created with allowUserCreation="false" (plugin-wide, all device types) so they no longer appear in the manual "New Device" type picker; auto-created switch/AP devices also carry an internal allowUserCreation=False prop marker
- mini-switch (no-SSH) override and per-device debug logging moved out of plugin config into each switch/AP device's own Edit Device dialog: new "Mini-switch (no SSH) override" menu (Auto / Force Mini / Force Regular) with a live auto-detected status label, and a "log debug msg from this device" checkbox; existing devices are migrated 1:1 from their old plugin-config values on first run (never silently reset to Auto)