A fork of the upstream Inovelli VZM32-SN Blue Series Zigbee
mmWave Dimmer driver that adds software-defined per-zone occupancy
attributes so the switch can drive scoped automations (e.g. "kitchen
sink area" vs "kitchen island area") without each consumer parsing the
raw targetInfo JSON blob.
Total diff against upstream is ~50 lines in three places:
- 4 new attributes on the device:
area0,area1,area2,area3. Each is a string"active"/"inactive". - 1 new preference:
zoneConfigJson— a JSON list (up to 4 entries) defining rectangular zones in the device's mmWave coordinate space. - A small block in
ReportTargetInfoCommandEventthat parseszoneConfigJsonon each report and emitsarea*events for any target that falls inside a defined zone. - One
log.infoin the per-target hot path quieted tolog.debug— it was firing once per second per target.
The change is otherwise non-intrusive: behavior of every other attribute
and command matches upstream. If zoneConfigJson is blank, the driver
behaves exactly like upstream.
The VZM32-SN reports an AnyoneInTheReportingArea boolean across the
whole field-of-view, plus (with parameter 107 enabled) a streaming
targetInfo blob with x/y/z coordinates of each detected target.
Upstream collapses the whole-area boolean into a single motion
attribute and dumps targetInfo as JSON for the consumer to parse.
That's fine for one switch but painful when you have a fleet of them
and want each switch to drive automations for areas within its
field of view (e.g. counter vs. island vs. sink).
This fork moves the zone-bucketing into the driver so consumers can
just subscribe to area0..area3 events.
The hardware can report up to 4 detection areas via the
AnyoneInTheReportingArea FC32 cluster command, but in current firmware
it OR-collapses them into the single boolean. Until Inovelli ships
per-area events natively, software bucketing of the targetInfo stream is
the only option.
When upstream gains real per-area events, this fork's value drops to ~zero and you can switch back to the upstream driver. (PRs to upstream to integrate this are welcome too.)
- HPM → Install → From a URL → paste:
https://raw.githubusercontent.com/rabidfurball/hubitat-vzm32-mmwave-zone-driver/main/packageManifest.json - Confirm install.
In Hubitat: Drivers Code → + New Driver → Import →
https://raw.githubusercontent.com/rabidfurball/hubitat-vzm32-mmwave-zone-driver/main/inovelli-vzm32-sn-zone.groovy
→ Import → Save.
-
Pair / discover the VZM32-SN as usual. Set its Type to Inovelli VZM32-SN mmWave Zone-Aware (drop-down on the device page).
-
Enable parameter 107 (
mmWave Target Info) in device preferences. This is the streaming x/y/z target feed — the driver needs it to compute zone occupancy. (Note: this generates ~1 event/second per target while motion is present. For a busy fleet, consider whether the polling load is acceptable.) -
Configure zones via the Zone Config (JSON) preference. Up to 4 entries. Coordinates are in millimeters; +x is right, +y is forward, origin is the switch.
Example for a kitchen switch with 4 functional zones:
[ {"name":"area0","xMin":-600,"xMax":600,"yMin":0, "yMax":130}, {"name":"area1","xMin":-600,"xMax":170,"yMin":130,"yMax":270}, {"name":"area2","xMin":170, "xMax":600,"yMin":130,"yMax":310}, {"name":"area3","xMin":-600,"xMax":600,"yMin":310,"yMax":600} ] -
Save. The
area0..area3attributes will start updating as targets move through your defined zones. Subscribe to them in Rule Machine, webCoRE, or whatever your automation engine of choice is.
Easiest workflow: enable targetInfo reporting, watch the JSON output
in the device's Current States for a minute while you walk around the
room. Each entry is {i, id, x, y, z, dop} in mm. Note the x/y values
in each functional area, then translate to rectangular bounds with a
bit of margin.
For a visualization tool that draws targets on a floor-plan overlay in real time (instead of squinting at a JSON blob), see the companion hubitat-vzm32-mmwave-radar-viz repo.
- One detection-area mismatch tolerated: the driver currently uses
the targetInfo stream's coordinate space, which is the device's full
field of view, NOT the user-defined detection areas configured via
mmWaveSetDetectionArea. Targets falling outside hardware-defined detection areas still appear intargetInfoand will trigger zones if bounds overlap. This is usually a feature (you can software-define zones the hardware doesn't). - No persistence: this fork tracks zone state in Hubitat attributes only, no battery-backed memory across hub restarts.
- One driver instance per VZM32-SN: zones are per-device; if you want shared zones across multiple switches, do the merge in your automation layer.
Fork is intentionally small — when upstream releases new features, the plan is:
- Pull the new upstream verbatim into a
upstream-mergebranch. - Re-apply this fork's patch (it's anchored on
attribute "targetCount"andsendEvent(name: "targetInfo"...)which are stable). - Bump version, push.
docs/upstream-diff.md contains the canonical patch as a reference for
manual merge.
- The driver source file (
inovelli-vzm32-sn-zone.groovy) is a fork of the upstream Inovelli driver and remains under Apache License 2.0 (header preserved in the file). - The repo wrapper (README, packageManifest, etc.) is MIT — see LICENSE.
- Eric Maycock (@erocm123) and Mark Amber (@marka75160) at Inovelli for the upstream driver.
- Built with Claude Code.