[RELEASE] Advanced Room Occupancy - Vampire Power Assassin & ROI Tracker

[RELEASE] Advanced Room Occupancy - Vampire Power Assassin & ROI Tracker

Hello everyone!

I am excited to release Advanced Room Occupancy, a multi-zone control application designed specifically to eradicate "vampire" (standby) power from your home, calculate your exact financial savings, and flawlessly handle the edge-cases that usually make room automation frustrating.

Standard motion-lighting apps are great for hallways, but they fall apart in complex rooms like Home Theaters and Offices. If you sit still to watch a movie, the room shuts off. If you use a smart plug to kill power to a PC or OLED TV, a sudden hard-kill can corrupt your OS or interrupt pixel-refresh cycles. If your pet walks by, the whole room boots up.

I built this app to solve all of those problems. It acts as a localized BMS (Building Management System) for up to 12 rooms, featuring two-stage shutdowns, power-monitoring failsafes, hit-counters, and mesh network protection.

:star2: Core Features

  • Precise Financial ROI Tracking: You aren't just guessing if you are saving money. Input your utility rate ($/kWh), and assign an Active/Idle Wattage and Standby/Phantom Wattage to every individual device. The app calculates exactly how much money you save every minute those devices are killed while the room is empty.
  • Two-Stage Graceful Shutdowns: Perfect for sensitive electronics. When a room empties, the app first sends a "Soft Kill" (network command) to devices like PCs or Roku TVs. It then waits for a customizable delay (e.g., 60 seconds) to let the OS shut down or the OLED screen run its maintenance before dropping the guillotine on the hard power relays.
  • Active Wattage Failsafe (The Ultimate Override): Tie a power-monitoring smart plug to a room and set a safe threshold (e.g., 15W). Even if the motion and vibration sensors time out, the app will refuse to shut down the room if your PC is downloading a massive update or your 3D printer is running.
  • Sliding Window "Hit Counters" (False-Positive Protection): Standard PIR sensors pulse constantly. Instead of requiring "X minutes of continuous motion," you can set a sliding window. (e.g., Require 3 motion hits within 2 minutes to activate). Someone running in to grab a jacket won't turn on the entertainment center, but someone sitting down at a desk will trigger it immediately.
  • Auto-Off Virtual Overrides: Need to force a room to stay on? Toggle the room's Virtual Override Switch. But to prevent the classic "I left it on and it ran all weekend" problem, the switch has a built-in Auto-Off timer that clears the override if the room remains completely dead of physical sensor activity for hours.
  • Staggered Mesh Execution: If you trigger a global "Force Unoccupied" mode (like Away or Good Night), the app won't crash your Z-Wave/Zigbee mesh by broadcasting 30 "OFF" commands at once. It intelligently staggers the room shutdowns with a 2.5-second delay.
  • System Boot Recovery Failsafe: If your house loses power or the hub reboots, physical relays often default to OFF, causing a state desync. This app listens for the systemStart event, clears out any corrupted countdown timers from RAM, and blindly fires a hardware sync to guarantee your physical plugs match the app's internal logic.

:bar_chart: Live Telemetry Dashboard

The app features a clean, collapsible UI. The main page generates a live dashboard showing every room's current state, exactly which sensors are holding it open, a live countdown to shutdown, real-time dynamic wattage tracking, and your total aggregated financial savings.


Disclaimer & Open Source

This application is 100% open source and completely free to use. You are welcome and encouraged to use it, edit it, fork it, or strip out any of the logic/code snippets for your own personal projects. My goal is just to share some advanced logic with the community to help everyone save power and protect their equipment!

raw.githubusercontent.com/ShaneAllen334/Hubitat_Apps/refs/heads/main/Advanced_Room_Occupancy/Advanced_Room_Occupancy.groovy

5 Likes

I'm getting the following error when trying to set up my first room.

Ill look into this. Thank you

1 Like

I issued the fix on Github. Update your code.

I apologize I did some UI changes and missed a few things earlier today.

1 Like

For zigbee devices, it would seem that this would be a good use for zigbee group messaging and a broadcast command.

How does this 2.5 second delay fit with the "command retry logic" backoff timer?

The 2.5-second stagger and the Zigbee/Z-Wave command retry logic operate at two different layers of the networking stack. Think of the stagger as Traffic Control (preventing a jam) and the retry logic as Insurance (fixing a collision).

1. Prevention vs. Correction

  • The App’s Stagger (Application Layer): When multiple rooms trigger a shutdown simultaneously (e.g., via a Mode Change), the app spacing ensures the Hubitat radio isn't asked to buffer and broadcast dozens of commands in the same millisecond. This prevents "Buffer Full" errors at the hub level and keeps the "Clear to Send" (CTS) window open for other high-priority traffic.
  • Retry Logic (MAC/Physical Layer): If a command does collide or fails to acknowledge (ACK), the radio hardware initiates its backoff timer (typically doubling in duration for each attempt, e.g., 10ms, 20ms, 40ms).

2. Fitting the Backoff Window

The 2.5-second delay is significantly longer than the standard hardware backoff window (which usually concludes within 100-500ms). By using a multi-second stagger:

  • We avoid "Retry Storms": If 30 devices fail at once, they all enter retry-backoff mode simultaneously, further congesting the airwaves. Spacing the initial commands ensures that if a specific device needs to retry, it has a "quiet" mesh to do so without competing against 29 other primary commands.
  • Reliability for Non-Grouped Devices: While Zigbee Group Messaging is highly efficient for identical bulbs, many "Hard Power" devices (like varied Z-Wave outlets or specific smart plugs) don't support common group casting. The stagger ensures these "individual talkers" don't overwhelm the mesh.

3. Why not use Zigbee Groups?

While the app is designed for maximum compatibility across brands and protocols, Zigbee Grouping is excellent for lighting. However:

  • Heterogeneous Meshes: Most users have a mix of Z-Wave and Zigbee. Group broadcasting doesn't work across different protocols.
  • Feedback Loops: Individual "OFF" commands allow the app to verify the state of each device. In a broadcast, the hub often misses the "Inbound State Report" from 20 devices reporting "OFF" at the exact same time. The 2.5s delay gives each device a window to report its new status back to the hub cleanly.

As you state, the purpose the stagger is to prevent collisions. If retry logic is still operating on the prior device, how do you prevent a collision with subsequent devices?

1. The Application Layer: "Macro-Staggering"

The app doesn't just "fire and forget" every command in a single millisecond. It uses a timed sequence to space out the heavy lifting:

  • Soft-Kill vs. Hard-Kill Separation: The app first sends "Soft Kill" commands to sensitive electronics. It then uses a user-defined HardKillDelay (in seconds) before cutting the main power relays.
  • Sequential Processing: Because the app iterates through rooms using a loop , the Hubitat hub naturally processes these commands in a sequence rather than a true parallel broadcast.

2. The Hardware Layer: "Listen Before Talk" (CSMA/CA)

Even if the app sends two commands nearly back-to-back, the Zigbee and Z-Wave radios are smart enough to avoid talking over each other.

  • Clear Channel Assessment (CCA): Before the hub's radio transmits, it "listens" to the frequency for a few microseconds.
  • The Wait: If it detects that another device is currently talking or retrying, the hub will pause its own transmission until the airwaves are clear.

3. The Protocol Layer: Random Backoff & Retries

If a collision does happen (which can occur if two devices start talking at the exact same microsecond), the protocol takes over:

  • Acknowledgement (ACK) Requirement: When the hub sends an OFF command, it waits for the device to say "I got it.".
  • Exponential Backoff: If the hub doesn't hear that "ACK," it waits a random, tiny amount of time (e.g., 10ms) and tries again. If it fails again, it waits longer (e.g., 40ms).
  • Prevention of "Retry Storms": By using a 2.5-second stagger at the app level, we ensure that the hub's radio buffer is empty before the next room's commands arrive. This gives the "Insurance" (retries) plenty of time to finish before the "Traffic" (new commands) starts back up.

I hope this answers your question, but simple answer there will be some stacking.

That did the trick for me. Thanks.

What's New in Advanced Room Occupancy

  • Granular Trigger Control: You now have complete, user-selectable control over exactly what turns a room ON and OFF. Independently mix and match between Virtual Switches, Motion Hit Counts, Continuous Motion, Vibration, and Presence Sensors.
  • Continuous Motion Engine: Want to completely eliminate false positives? You can now require a specific duration of uninterrupted motion (e.g., 3 minutes of continuous reading) before a room is marked as occupied. If the motion drops out for even a second, the timer resets.
  • Restricted Mode "Deep Clean" Purge: When a room enters a restricted mode, the app now performs a total system wipe for that zone. All motion/vibration hit counts, continuous motion timers, and pending schedules are instantly purged, and virtual override switches are completely disabled to ensure a perfectly clean slate when the room becomes active again.

Get the latest code here.

raw.githubusercontent.com/ShaneAllen334/Hubitat_Apps/refs/heads/main/Advanced_Room_Occupancy/Advanced_Room_Occupancy.groovy

:rocket: Advanced Room Occupancy Update: Granular Triggers, UI Decluttering & Failsafes

:dart: Granular, Selectable Triggers You now have complete, user-selectable control over exactly what turns a room ON and OFF. You can independently mix and match between Virtual Switches, Motion Hit Counts, Continuous Motion, Vibration, and Presence Sensors.

:stopwatch: Continuous Motion Engine (The Anti-False Positive) You can now require a specific duration of uninterrupted motion (e.g., 3 minutes of continuous movement) before a room is marked as occupied. If the motion drops out for even a second, the timer completely resets.

:bulb: State Reason Engine (Total Transparency) The Live Occupancy Dashboard now shows exactly why a room is in its current state. No more guessing why a light is still on—the dashboard will explicitly tell you "Motion Duration Reached," "Virtual Switch Enabled," or "Vibration Timer Active."

:radio_button: Physical Button Linking You can now link a physical button directly to a room's configuration. Pushing the button seamlessly toggles the room's Virtual Override Switch, ensuring your physical wall remotes and the app's virtual occupancy timers stay perfectly in sync.

:soap: Dynamic Dashboard Decluttering The Live Dashboard is now context-aware! If a specific sensor or timer isn't selected as an active trigger for a room, it is completely hidden from that room's dashboard view. You only see the data that is actively driving your logic.

:zap: Global System Overrides Added two new "panic buttons" in the Global Configuration section:

  • Force ALL Rooms OCCUPIED: Instantly turns on all active rooms.
  • Force ALL Rooms EMPTY: Instantly purges all timers and initiates the shutdown sequence for every room in the house.

:hammer_and_wrench: Deep Purge & Anti-Ghosting Failsafes (Under the Hood) We completely rebuilt how the app handles memory and state maps to bypass known platform bugs with ghost timestamps.

  • Restricted Mode Deep Clean: When a room enters a Restricted Mode, the app performs a total system wipe for that zone (purging all hit counts, continuous timers, and virtual switches) so it wakes up with a clean slate.
  • Routine Sweeper: The app now runs a background sanity check to guarantee that if a physical sensor is inactive, no "ghost" motion timers can get trapped in the hub's database, completely eliminating the "stuck occupied" logic loop.

raw.githubusercontent.com/ShaneAllen334/Hubitat_Apps/refs/heads/main/Advanced_Room_Occupancy/Advanced_Room_Occupancy.groovy

Hey everyone, just pushed a new update to Advanced Room Occupancy. Since a primary focus of this app is hunting down and killing vampire power from AV equipment, PCs, and TVs, this release brings some aggressive new features to catch stranded power, along with a few UI and stability improvements.

:sparkles: New Features & Changes

  • Unoccupied Sweeper (Manual Mode Backup): Added a safety net for stranded devices. If a managed device is manually turned on (via a physical wall switch, Alexa, or another app) while the room is officially EMPTY, the Sweeper activates. If no motion is detected for a customizable duration (default 60 minutes), it automatically executes the shutdown sequence. Any new motion or physical switch press instantly resets the Sweeper countdown.
  • Adjustable Motion Grace Period: Say goodbye to rooms accidentally shutting down because a sensor blinked off for a few seconds. You can now define a specific grace period (default 15 seconds) that sensors must remain inactive before the app begins processing the empty timeouts.
  • Room Good Night Switch: Added a dedicated virtual switch to serve as an ultimate occupancy lock. When turned ON, the room is forced to stay OCCUPIED and will completely ignore all empty timeouts and global mode changes until the switch is turned back off.

:chart_with_upwards_trend: Dashboard & UI Improvements

  • Live Sensor Tracking (Occupied State): The dashboard hit counters and continuous motion durations no longer disappear once a room becomes occupied. They now stay fully visible, allowing you to easily verify that your sensors are accurately tracking background motion and tracking total active durations in real-time.
  • Dynamic UI Clean-up: The motion hit counter on the dashboard will now dynamically hide itself if "Motion Hit Count" is not actively selected as a trigger in the room's settings.

:bug: Bug Fixes

  • Groovy Type Mismatch Fix: Resolved a silent background crash where the integer hit counts from the sensors were failing to evaluate against string inputs from the database. The internal engine now correctly forces type conversion (.toInteger()), ensuring that the background math perfectly aligns with what the dashboard displays.

raw.githubusercontent.com/ShaneAllen334/Hubitat_Apps/refs/heads/main/Advanced_Room_Occupancy/Advanced_Room_Occupancy.groovy

:star2: New Features & Additions

  • Absolute Sweeper (Restriction Bypass): Added a powerful new failsafe to prevent wasted energy during "Paused" or Restricted modes (like parties or guest modes). Even if room rules are paused, the Absolute Sweeper monitors for stranded devices left ON and will force a shutdown if absolutely zero motion, vibration, or presence is detected for the configured long-tail timeout.
  • Individual Room UI Overrides: Added dedicated "Force EMPTY" and "Force OCCUPIED" buttons to the dashboard for every active room. You can now instantly brute-force a specific room's state and hardware without affecting the rest of the house.
  • Live Dashboard Refresh Button: Added a "Refresh Live Data" button to the top of the dashboard. You can now pull the absolute latest state variables, wattages, and timers into the UI table without having to back out and reload the entire application page.

:hammer_and_wrench: Improvements & Changes

  • True Master Kill Switch: Re-engineered the Master Enable/Disable switch to act as a strict hard-abort. Toggling the master switch OFF now instantly kills all pending shutdown sequences, purges active schedules, and drops all incoming sensor data at the door, completely freezing the app's influence over your home.

:bug: Bug Fixes

  • Ghost Event Guard (Event Bus Latency Fix): Resolved an issue where hub processing latency was causing automated Virtual Switch "ON" commands to be misidentified as physical manual button presses, which inadvertently locked rooms into long manual override timers.
    • Technical fix: Increased the command verification window to 15 seconds and added a duplicate-event guard that rejects late incoming events if the room has already successfully reached an auto-occupied state.

raw.githubusercontent.com/ShaneAllen334/Hubitat_Apps/refs/heads/main/Advanced_Room_Occupancy/Advanced_Room_Occupancy.groovy

Bug fixes for Advanced Room Occupancy are available on Github.