canbus: furnace + water-heater status sensors (running + DSI fault)
Decode node 89 (furnace) read-only: Furnace Running (page-3 b0 bit0) and Furnace DSI Fault (b0 bit5), same type-0x1E DSI encoding as the water heater. The furnace is thermostat-controlled, not a Lippert load — it sits on the bus only to report ignition state. Captures only ever showed 0x80 (off), so the 0x81 running / 0xA0 fault bytes are inferred by parallel to node 95; confirm on the first real burn. Add Water Heater Heating (node 95 page-3 x[3] bit7 = burner actively firing) — distinct from the on/off switch, which only reflects "enabled". Carry all three through the MQTT bridge to home HA, add the furnace DSI-fault Octavia alert mirroring the water heater, and update the README node map (89 was "unknown switched load"). Campsite + home Camper dashboards updated live. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -172,8 +172,24 @@ canbus:
|
||||
// water heater (node 95): b0 bit0 = on, bit5 (0x20) = DSI/gas
|
||||
// ignition fault/lockout (healthy: 0x80 off / 0x81 running;
|
||||
// fault: 0xA0). Confirmed by a forced lockout 2026-06-12.
|
||||
// x[3] bit7 = burner ACTUALLY FIRING (vs merely enabled): idle
|
||||
// reads ...00 01, a live burn reads ...00 9X with the low nibble
|
||||
// climbing as it heats (captured 2026-06-11). The switch state
|
||||
// above is "enabled"; wh_heating is "actually making heat".
|
||||
case 0x95: id(water_heater).publish_state(x[0] & 0x01);
|
||||
id(dsi_fault).publish_state(x[0] & 0x20); break;
|
||||
id(dsi_fault).publish_state(x[0] & 0x20);
|
||||
if (x.size() >= 4) id(wh_heating).publish_state(x[3] & 0x80);
|
||||
break;
|
||||
// furnace (node 89): propane forced-air, THERMOSTAT-controlled
|
||||
// (not a Lippert load) — on the bus only to report DSI state, in
|
||||
// the same type-0x1E encoding as the water heater. b0 bit0 =
|
||||
// running, bit5 (0x20) = DSI ignition lockout. A lockout also
|
||||
// trips the bus-wide page-0 fault flag (system_fault). Only idle
|
||||
// 0x80 was captured (summer bench); 0x81 running / 0xA0 fault are
|
||||
// inferred by parallel to node 95 — confirm on the first real
|
||||
// burn cycle (or a forced lockout, propane off).
|
||||
case 0x89: id(furnace_running).publish_state(x[0] & 0x01);
|
||||
id(furnace_dsi_fault).publish_state(x[0] & 0x20); break;
|
||||
// awning H-bridge (node 75): b0 C0 idle / C2 extending (opening)
|
||||
// / C3 retracting (closing). Reflect motion onto the cover so HA
|
||||
// shows open/opening/closing; assumed_state fills the resting pos.
|
||||
@@ -347,6 +363,18 @@ binary_sensor:
|
||||
name: "Water Heater DSI Fault"
|
||||
id: dsi_fault
|
||||
device_class: problem # node 95 page-3 b0 bit5 — gas ignition lockout
|
||||
- platform: template
|
||||
name: "Water Heater Heating"
|
||||
id: wh_heating
|
||||
device_class: running # node 95 page-3 x[3] bit7 — burner actively firing
|
||||
- platform: template
|
||||
name: "Furnace Running"
|
||||
id: furnace_running
|
||||
device_class: running # node 89 page-3 b0 bit0 — thermostat-driven burn
|
||||
- platform: template
|
||||
name: "Furnace DSI Fault"
|
||||
id: furnace_dsi_fault
|
||||
device_class: problem # node 89 page-3 b0 bit5 — gas ignition lockout
|
||||
- platform: template
|
||||
name: "OneControl System Fault"
|
||||
id: system_fault
|
||||
@@ -368,11 +396,18 @@ binary_sensor:
|
||||
# state (verified live 2026-06-12), so HA shows ground truth — a dropped command
|
||||
# self-corrects within ~1 s instead of an optimistic echo falsely reporting success.
|
||||
# ---------------------------------------------------------------------------
|
||||
# restore_mode: DISABLED on all three is load-bearing: ESPHome's default
|
||||
# restore "applies" the boot state BY EXECUTING THE SWITCH ACTION, which sent
|
||||
# a real authenticated OFF to the water heater on the 2026-06-12 OTA reboot
|
||||
# (send_load_command is mode:restart, so the last switch in setup order — the
|
||||
# heater — won the race). Boot must send nothing: the broadcasts repopulate
|
||||
# every state within ~1 s and are the only source of truth.
|
||||
switch:
|
||||
- platform: template
|
||||
name: "Exterior Lights"
|
||||
id: exterior_lights
|
||||
optimistic: false
|
||||
restore_mode: DISABLED
|
||||
turn_on_action:
|
||||
- script.execute: { id: send_load_command, node: 0x2A, op: 1 }
|
||||
turn_off_action:
|
||||
@@ -382,6 +417,7 @@ switch:
|
||||
name: "Interior Lights"
|
||||
id: interior_lights
|
||||
optimistic: false
|
||||
restore_mode: DISABLED
|
||||
turn_on_action:
|
||||
- script.execute: { id: send_load_command, node: 0xF8, op: 1 }
|
||||
turn_off_action:
|
||||
@@ -391,6 +427,7 @@ switch:
|
||||
name: "Water Heater"
|
||||
id: water_heater
|
||||
optimistic: false
|
||||
restore_mode: DISABLED
|
||||
turn_on_action:
|
||||
- script.execute: { id: send_load_command, node: 0x95, op: 1 }
|
||||
turn_off_action:
|
||||
|
||||
Reference in New Issue
Block a user