canbus: replace BLE integration with CAN node in HA + MQTT bridge home

The campsite HAOS Pi now runs the ESPHome CAN node natively ("OneControl
CAN", *.onecontrol_can_*); the BLE integration is fully torn out (config
entry, custom_components on the Pi, stale MQTT registry orphans) and the
camper dashboard rewritten — water-pump tile dropped on purpose (pump is
panel-only), awning + fault sensors added.

New canbus/ha/mqtt_bridge_onecontrol.yaml (deployed to the Pi as
packages/mqtt_bridge.yaml) bridges the CAN entities to the home broker
via MQTT Discovery, same pattern as the gazebo bridge. Kept the old
unique_ids so home entity ids and recorder history carried over; added
an availability topic and explicit command-topic allowlists (3 switches
+ awning — a future debug entity must not become remotely controllable
for free). Round-trip verified from home HA.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
wes
2026-06-12 15:32:12 -04:00
co-authored by Claude Fable 5
parent e8b2447d62
commit 9a5ee30db9
2 changed files with 431 additions and 18 deletions
+67 -18
View File
@@ -9,6 +9,32 @@ Read `README.md` first — it has the full message-format documentation, the nod
map for this coach, and the wiring procedure. This file is just the current
state + what's left.
## FLASHED & READ-VERIFIED (2026-06-12, afternoon)
The node is built, flashed over USB, on the bus, and on WiFi
(`192.168.69.18`, hostname `onecontrol-canbus`, adoptable via the API key in
`secrets.yaml`). **All 11 read entities verified live over the native API**:
battery 13.27 V, tanks fresh 0 / black 33 / grey1 33 / grey2 100 %, interior
lights on, exterior/heater off, both fault flags clear, awning idle.
What changed this session:
- `secrets.yaml` filled (WiFi + fallback AP from the shared gazebo proxy creds).
- **Battery voltage decode finished** (remaining-work item 4): matches the
page-`0x11` `00 2B …` telemetry frame, `b2..b3` BE / 256 — reads 13.27 V.
- **Toolchain fixes** (ESPHome moved since the last build; it no longer compiled):
the `g_node_type` array global's bare `{}` initializer was ambiguous (named the
type), and the awning cover enum is `COVER_OPERATION_OPENING/CLOSING` (no `IS_`).
- **Dual `on_frame` trigger** — this build filters `on_frame` by frame type, so a
single `use_extended_id: true` trigger only saw the 29-bit frames; the 11-bit
read broadcasts (tanks/lights/heater/awning) were silently dropped. Added a
second `use_extended_id: false` trigger sharing the same lambda via a YAML
anchor. (This was the exact risk the bus-config note called out.)
- **Logger** — raised the `canbus` component to INFO and silenced the per-frame
dump; at DEBUG it logged ~50 frames/s and saturated the 115200 serial link.
Still TODO (needs the operator in the loop — see "Remaining work" items 56):
command-path verification (toggle a light) and the attended awning test.
## Current status (2026-06-12)
- **Message format documented and confirmed against live captures.** The bus is
@@ -42,31 +68,54 @@ state + what's left.
## Remaining work (in order)
1. **`secrets.yaml`** — copy `esphome/secrets.yaml.example`, fill in WiFi, the
ESPHome `api_key`, and the fallback-AP password. It's git-ignored.
2. **First flash over USB:** `esphome run esphome/onecontrol-canbus.yaml` (pick
the serial port). OTA after that. Mirrors the gazebo-fan-proxy workflow.
3. **Confirm the read entities populate** in HA once it's on the bus: the four
tanks and the two light switches publish from the page-3 broadcasts. Watch the
DEBUG frame dump (`logger: level: DEBUG`) to confirm frames are decoded; drop
to INFO when happy.
4. **Finish the two open read items in the YAML lambda:**
- **Battery voltage** — rides a 29-bit telemetry frame (src `7D`/`AE`,
page `0x11`), bytes 23 big-endian / 256 = volts. Match that frame and
publish `battery_voltage`. (See README "29-bit extended frames".)
1. ~~**`secrets.yaml`**~~ — DONE. WiFi + fallback-AP creds filled from the shared
gazebo proxy; the `api_key` was already set.
2. ~~**First flash over USB**~~ — DONE (`/dev/ttyUSB0`). OTA from here on.
Note: this laptop's user isn't in the `uucp` group, so the serial node needed
`chmod 666 /dev/ttyUSB0` (resets on replug) — irrelevant for OTA.
3. ~~**Confirm the read entities populate**~~ — DONE. All 11 verified live over the
native API (see status block above). NB: ESPHome 2026.5 doesn't echo
`publish_state` at DEBUG over serial, so a quiet serial log is normal — read the
states over the API, not the console.
4. **Open read items in the YAML lambda:**
- ~~**Battery voltage**~~ — DONE; reads 13.27 V.
- Optionally add **water pump (`61`)** and **water heater (`95`)** — both are
ordinary switched loads, same decode + command path as the lights.
5. **Verify the command path** from HA: toggle Interior/Exterior Lights and the
Water Heater. The `send_load_command` script + `on_frame` handler do the
exchange and send the opcode.
ordinary switched loads, same decode + command path as the lights. (Heater is
already exposed; pump stays command-blocked.)
5. ~~**Verify the command path**~~ — DONE (Interior + Exterior lights, on & off,
each a confirmed challenge→response→opcode exchange on the serial log, with the
read-back flipping to match). Switches are now `optimistic:false` so HA shows
ground truth. **Confirmed module behaviour:** after a successful session the
module enforces a **~2 s cooldown** — a second command to the same load sooner
gets no challenge and is dropped (opcode-independent, module-side, not a bug).
The arm retry was widened to 8×150 ms to absorb the odd dropped frame on the
busy bus. Heater is wired the same way but wasn't actuated (avoid cycling gas
ignition casually); it should behave identically.
6. **Attended awning test (motor — watch it move).** The Awning `cover` is wired
(open/close/stop). On the first run, confirm: (a) which direction open/close
actually go, (b) whether one command runs to the travel limit or only moves
while commands stream. If it under-travels, change `send_load_command` to
stream the opcode (repeat until Stop) — only after watching it. The single-shot
default can't run the motor away.
7. **(Optional) Surface at the campsite HA + bridge home** like the gazebo fans /
OneControl BLE devices, if you want these in the home dashboard too.
7. ~~**Surface at the campsite HA**~~ — DONE (2026-06-12). Added to the campsite
HAOS Pi (`192.168.69.10`) as ESPHome config entry "OneControl CAN"; all 11
entities (`*.onecontrol_can_*`) live. The old BLE integration is **fully torn
out**: config entry deleted, `custom_components/lippert_onecontrol` removed
from the Pi, 11 stale `campsite_onecontrol_*` MQTT registry orphans purged
(home broker had no retained discovery topics — they were registry-only). The
camper dashboard (`lovelace.dashboard_camper`) was rewritten to the new
entities; the water-pump tile was dropped (pump is panel-only by design) and
awning + both fault sensors added.
8. ~~**Bridge home via MQTT**~~ — DONE (2026-06-12 evening). The Pi package
`/config/packages/mqtt_bridge.yaml` was rewritten against the CAN entities
(repo copy: `canbus/ha/mqtt_bridge_onecontrol.yaml`); it publishes MQTT
Discovery + state to cyrion's Mosquitto (192.168.88.69) and relays commands
back, same pattern as the gazebo bridge. Same `unique_id`s kept for surviving
loads, so home HA entity ids + history carried over (`*.campsite_onecontrol_*`);
water pump / cover_2 / cover_3 retained topics cleared (entities auto-removed
on home); awning + both fault sensors added, plus an availability topic
(`campsite/onecontrol/availability`) the old bridge lacked. Home Overview
"Camper" view updated to match. Command round-trip verified from home HA.
The DSI fault is already decoded and wired in (see below) — no capture needed.