docs: showpiece README — mermaid architecture, sensor/alert tables [#762]

Also absorbs PFVCluster operator tooling: tools/ha-ws-call.py and
docs/reference-packs/ (historical, NOT loaded by HA — clearly marked).
No live-config changes in this commit.

Detail: https://projects.knownelement.com/issues/762
This commit is contained in:
2026-09-03 13:34:53 -05:00
parent 96e70aa231
commit 6c5efbb609
5 changed files with 699 additions and 31 deletions
+150 -31
View File
@@ -1,45 +1,164 @@
# pfv-bms — Home Assistant configuration (Turnsys PFV)
# 🏠 pfv-bms — the house brain
Production Home Assistant configuration for a private residence +
server room: physical-plant environmental monitoring (temperatures,
power, UPS), server-room badge access control, and alerting.
> **Datacenter-grade monitoring and access control for a private residence + server room** —
> 372 entities, ~260 sensors, and a badge-actuated door, all deployed by `git push`.
Licensed under the [GNU AGPLv3](LICENSE) — see [AGPL compliance
notes](#license) below.
![license](https://img.shields.io/badge/license-AGPLv3-blue)
![HA](https://img.shields.io/badge/Home%20Assistant-2026.8.3-41bdf5?logo=homeassistant&logoColor=white)
![HAOS](https://img.shields.io/badge/HAOS-18.2-orange)
![deploy](https://img.shields.io/badge/deploy-git%20push%20%E2%86%92%20live-4EAA25)
![entities](https://img.shields.io/badge/entities-372-9c27b0)
## What lives here
Production Home Assistant configuration for the Turnsys PFV —
environmental monitoring (temperatures, power, UPS) across a seven-node
Proxmox fleet plus single-board computers, server-room badge access
control, and tiered alerting to mobile devices.
- `configuration.yaml` + `packages/` — the full running config,
deployed to the HAOS box via a git-webhook pipeline
- `packages/doorman.yaml` — badge-scan ingest + whitelist decisions
(server-room door access)
- `packages/plant_snmp.yaml` — fleet temperature/power sensors over
SNMP (lm-sensors extends, iDRAC, UPS, PDU relay)
- `automations.yaml` — tiered thermal alerts + sensor watchdogs
- `dashboards/` — Lovelace dashboards
Work tracking: [Redmine](https://projects.knownelement.com)
([#344](https://projects.knownelement.com/issues/344) umbrella) ·
Docs: [VP TechOps on Discourse](https://community.turnsys.com/c/vp-techops)
## Security model (read this before forking)
---
## 🗺️ System architecture
```mermaid
flowchart LR
subgraph FIELD[📡 The field]
TS[🖥️ 7× Proxmox hosts<br/>lm-sensors + iDRAC extends]
BOARDS[🥧 SBCs<br/>pfvsvrpi · jetson · 3× subopi]
UPS[🔋 UPS on tsys1<br/>NUT server :3493]
PDU[🔌 Garage PDU<br/>SNMPv1 relay]
METER[⚡ Utility meter<br/>Smart Meter Texas]
READER[🪪 Badge readers<br/>pfvsvrpi · ultix-field]
end
subgraph HA[🏠 pfv-bms — this repo]
INGEST[📦 packages/plant_snmp.yaml<br/>SNMP pollers → raw sensors]
TEMPLATE[🧮 template sensors<br/>raw → °F/units + thresholds]
DOOR[🚪 packages/doorman.yaml<br/>scan ingest + decision engine]
ROSTER[🔒 roster sensors<br/>REST poll of private access-roster]
ENERGY[📊 utility_meter +<br/>Riemann-sum energy]
AUTO[🚨 automations.yaml<br/>tiered thermal + UPS alerts]
end
subgraph OUT[📲 Outcomes]
ALERT[📱 push alerts<br/>companion apps]
LOG[📓 logbook + history<br/>every scan, every degree]
DASH[🖥️ dashboards<br/>server-room + plant]
RELAY[⚡ doorctl → usbrelay<br/>→ door strike]
end
READER -->|webhook| DOOR
DOOR -->|whitelist| ROSTER
DOOR -->|armed + valid| RELAY
TS & BOARDS -->|SNMP| INGEST
UPS -->|NUT| INGEST
PDU -->|SNMP relay| INGEST
METER -->|REST| INGEST
INGEST --> TEMPLATE --> AUTO
INGEST --> ENERGY
AUTO --> ALERT
TEMPLATE & DOOR --> LOG & DASH
```
### 🛡️ Badge access lane (server-room door)
| Component | Role | Lives |
|-----------|------|-------|
| USB HID readers | 13.56 MHz badge readers (keyboard-emulating) | door + dev bench |
| [`KNEL/doorman`](https://git.knownelement.com/KNEL/doorman) | pure-bash listener, decode, webhook, doorctl | `pfvsvrpi` (prod), `ultix-field` (dev) |
| `packages/doorman.yaml` | scan ingest, whitelist decision, alerts, unlock dispatch | **this repo** |
| [`KNEL/access-roster`](https://git.knownelement.com/KNEL/access-roster) 🔒 | badge IDs + holders — **private**, two-human PR gate | gitea (private) |
| `doorctl` + usbrelay | socket-activated, token + source-IP ACL, EXIT-trap secured | `pfvsvrpi` |
Fail-closed end to end: HA unreachable → the door does not open.
Break-glass badges fire the relay locally (works with HA down) and
report `local_unlock` so HA never double-fires. Arm switch
(`input_boolean.doorman_unlock_enabled`) is founder-level control.
Verified end-to-end 2026-09-03: real armed scan → dispatch → relay.
---
## 🌡️ Environmental monitoring
Raw SNMP pollers (`packages/plant_snmp.yaml`) → unit-normalized
template sensors → tiered automations. Every host contributes
lm-sensors extends; iDRAC hosts add inlet/exhaust/CPU; NUT feeds UPS
electricals.
| Family | Coverage |
|--------|----------|
| 🖥️ Host silicon | CPU/drive/NVMe/PCH temps on 7 Proxmox hosts |
| 🌡️ Thermal zones | rack ambient, DIMM banks, GPU, tsys7 iDRAC inlet→exhaust ΔT |
| 🥧 Boards | pfvsvrpi, jetson, 3× subopi |
| 🔋 UPS | tsys1 battery charge/voltage, on-battery + runtime alerts |
| ⚡ Power | whole-house kWh (utility), tsys6/7 node watts + kWh integrals, garage PDU amps |
| 🏠 Rooms | temp/humidity/battery sensors (Govee, cloud-fed) |
### 🚨 Alert tiers (automations.yaml)
| Trigger | Threshold |
|---------|-----------|
| Drive / DIMM temps | > 140 °F |
| NVMe | > 150 °F |
| PCH | > 160 °F |
| GPU | > 185 °F |
| Ambient | > 104 °F |
| UPS | on-battery transitions + runtime |
| Watchdog | plant sensor freshness sweep every 30 min |
### 📊 Snapshot (2026-09-03)
372 entities · ~260 sensors · 45 temperature sensors · 21
electrical/energy sensors · 8 todo lists · 2-way alerting to companion
apps.
---
## 🔐 Security model (read this before forking)
- **No secrets in git, ever.** Everything sensitive is `!secret`
indirection into an on-box `secrets.yaml` (gitignored). Verified by
full-history scans.
- **Badge IDs are NOT in this repo.** The badge roster lives in a
separate *private* repository (`access-roster`, two-human PR gate)
and is fetched live at runtime via the gitea API with a secret auth
header. This repo contains only the decision logic.
- Battery-powered room sensors and lock integrations are cloud-fed;
their credentials also live in `secrets.yaml`.
- **Badge IDs are NOT in this repo.** The roster lives in the *private*
[`KNEL/access-roster`](https://git.knownelement.com/KNEL/access-roster)
repo (two-human PR gate) and is fetched live at runtime.
- **Fail-closed access control** — see the badge lane above.
- Battery room sensors and locks are cloud-fed; credentials also live
in `secrets.yaml`.
## Related
## 🚀 Deployment pipeline
- Work tracking: [Redmine project 55](https://projects.knownelement.com)
([#344](https://projects.knownelement.com/issues/344) HA integration,
[#345](https://projects.knownelement.com/issues/345) badge access,
[#762](https://projects.knownelement.com/issues/762) publication)
- Documentation:
[VP TechOps on Discourse](https://community.turnsys.com/c/vp-techops)
- Badge listener/relay code: [KNEL/doorman](https://git.knownelement.com/KNEL/doorman)
- Wider fleet (Proxmox, DNS, monitoring): [KNEL/PFVCluster](https://git.knownelement.com/KNEL/PFVCluster)
```mermaid
flowchart LR
PUSH[📝 git push] --> HOOK[gitea webhook<br/>fast path]
PUSH --> SHA[sha-watch<br/>safety net · 300 s poll]
HOOK & SHA --> CHECK{changed files?}
CHECK -->|packages/**| RESTART[♻️ core restart<br/>webhook triggers need it]
CHECK -->|other YAML| RELOAD[⚡ reload only<br/>~5 min to live]
```
Gate before pushing `packages/**`: `ha core check` must pass on-box.
Measured end-to-end: 4 m 52 s commit → live.
## 🗺️ Repo map
| Path | What |
|------|------|
| `configuration.yaml` | entrypoint + package includes |
| `packages/doorman.yaml` | 🚪 badge scan ingest + decisions |
| `packages/plant_snmp.yaml` | 🌡️ SNMP pollers + templates |
| `automations.yaml` | 🚨 tiered alerts + watchdogs |
| `dashboards/` | Lovelace dashboards |
| `tools/` | operator tooling (websocket CLI) |
| `docs/reference-packs/` | historical YAML references — NOT loaded |
## 🔗 Related
- Badge listener + relay code: [`KNEL/doorman`](https://git.knownelement.com/KNEL/doorman)
- Wider fleet (Proxmox, DNS, monitoring): [`KNEL/PFVCluster`](https://git.knownelement.com/KNEL/PFVCluster)
- Badge roster (private): `KNEL/access-roster` 🔒
## License