Context for developing and debugging Hubitat Elevation apps, drivers, and hub environment — sandbox constraints, lifecycle idioms, capability contracts, plus grounded deploy/log-tail/lint mechanisms.
—
—
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
High
Do not use without reviewing
Captured live on 2026-07-15 against the "Apps" hub (C-8 Pro, zwaveJS backend, Z-Wave LR) by
tailing ws://<ip>/zwaveLogsocket (skills/_scripts/hub_radiolog.py) through a real inclusion and a real
graceful exclusion of a Zooz LR leak sensor. These are the signatures the mesh-health skill uses
to confirm a lifecycle event happened — the tooling observes and confirms, it does not trigger
(inclusion/exclusion is hub-UI + physical, with no endpoint to trigger it; force-remove of a FAILED
orphan and a full network rebuild are groundable — skills/_reference/endpoints.md).
LR devices join via SmartStart, verified against Silicon Labs: the device DSK/QR is added to
the hub's provisioning list ahead of time, and the device auto-includes on power-up — no
inclusion mode, no button press. Confirmed on the wire (new node 374, id ≥ 256 = LR):
Security2CCMessageEncapsulation, security class: S2_Authenticated (LR is S2-mandatory).WakeUpCCIntervalReport (a sleepy leak
sensor reported 43200 s / 12 h), VersionCCReport, ManufacturerSpecificCCReport
(manufacturer 0x027a = Zooz), BatteryCCReport, AssociationCCSet (lifeline group 1 → node 1).failed to decode... retrying with SPAN extension → Security2CCNonceReport SOS: true → the hub sends a fresh SPAN sender-EI.[DRIVER] line (RSSI: -NN dBm).Confirm inclusion: the snapshot node count rises and a new id (≥ 256 for LR) appears; the node's
nodeState settles to OK with security: S2_Authenticated.
Even an LR node excludes via the classic RemoveNodeFromNetwork API (verified — the node id
appears in the callback bytes). Signature, in order:
[DRIVER] hub: « [REQ] [RemoveNodeFromNetwork] # hub entered exclusion mode
[SERIAL] 0x...4b1a03 0176 07... status 0x03 (node found), node 0x176 = 374
[SERIAL] 0x...4b1a06 0176 07... status 0x06 (remove DONE), node 0x176
[CNTRLR] hub: the exclusion process was stopped
[CNTRLR] hub: Node 374 was removed # THE event lineThen zwaveJS purges the node's value DB ([-] <CC> ... (was ...) teardown lines) — that is the
aftermath, not the exclusion. Read the Node <n> was removed line, not the teardown.
Confirm exclusion: Node <n> was removed on the wire and the node id drops out of the snapshot.
Graceful removal of a SmartStart device is two steps: remove it from the provisioning list (else it re-includes on next power-up), and exclude/factory-reset the physical device.
RemoveFailedNode is the force-remove for a node the controller has marked FAILED; it applies only
to an orphan ghost (FAILED with no bound deviceId), never a recoverable real device. Its exact
wire signature was not captured this session (we declined to remove real devices), so it is not
asserted here — capture it before documenting the frames.
Every hub→device Z-Wave command emits a [DRIVER] hub: TransmitReport line the JSON snapshot never
shows. hub_radiolog.py parses it into a transmit sub-dict (and --summary medians it). Real
line and fields:
callback id: 244 transmit status: OK, took 40 ms routing attempts: 2 ... TX power: 14 dBm
ACK RSSI: -91 dBm measured noise floor: -92 dBm ← at the HUB
measured RSSI of ACK from destination: -85 dBm measured noise floor by destination: -96 dBm ← at the DEVICEstatus (OK / NoAck — the definitive send result), took_ms (real latency), routing_attempts (retransmits), tx_power (max US LR is 14 dBm).hub_noise_floor + hub_ack_rssi → hub_snr (device→hub headroom, measured at the controller).dest_noise_floor + dest_rssi → dest_snr (hub→device headroom, measured at the device).hub_snr well below dest_snr means the hub's own receiver is the bottleneck (its RF environment — co-located radios, USB3, a gear cluster), not the device and not distance. The opposite asymmetry points at the device end. This is the one measurement that localizes a flapping/high-latency link; the snapshot's single lwrRssi cannot.+78) are dropped — a received RSSI on these logs is always negative dBm.hub_snr ~1–8 dB while dest_snr ~11–15; 8 LR devices flapped OK↔FAILED with a 100 ms latency tail. Moving the hub to a quiet room dropped the noise floor to median −97 (spikes gone, worst −94), eliminated the retransmits and the latency tail (max took_ms 100 → 10 ms), and recovered all 8 — even though the devices' own signal got weaker. The noise floor, not the signal, was the fault.A nodeState: FAILED node with a bound deviceId is a real device currently unreachable (may
be transient — recover it, do not delete). Only a FAILED node with no deviceId is an orphan
ghost. Verified live: 8 nodes went FAILED during a session of heavy LR-channel activity, all with
deviceIds — real devices knocked offline, not ghosts. hub_mesh.py tags each failure_kind.