canbus: hard safety gate — slides/jacks are control-panel only, never over CAN

Per policy, the firmware must never command a slide or jack. command_guard.h adds
command_blocked(node, type): refuses any motor-class (0x21) node except the
awning (0x75), with an explicit slide/jack denylist (6A/7F/9C) that holds even
before a node's page-2 identity is observed.

Enforced in two independent places — the command-entry script (send_load_command)
and the actual transmit point (on_frame, right before TX) — so loosening one
can't open the other. Adding a slide/jack switch entity cannot actuate it. Node
device class is learned from page-2 broadcasts (g_node_type). Predicate
unit-tested 9/9 (host g++). Switch comments + HANDOFF safety notes updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wes
2026-06-12 11:34:21 -04:00
co-authored by Claude Opus 4.8
parent d57c2b21d2
commit f05203b9e3
3 changed files with 93 additions and 10 deletions
+31
View File
@@ -0,0 +1,31 @@
#pragma once
// Command-path safety policy for the OneControl integration.
//
// POLICY: slides and jacks are operated from the physical control panel ONLY,
// never over CAN. command_blocked() returns true for any node this firmware must
// never send a command to. It is the single source of truth for that rule and is
// checked in two independent places (the command-entry script and the actual
// transmit point), so loosening one does not open the other.
//
// What it blocks:
// * an explicit denylist of this rig's known slide / jack nodes (6A/7F/9C) —
// always blocked, even before we've heard their identity broadcast;
// * any movement/motor-class node (device class 0x21) other than the awning
// (0x75) — generalizes the rule to "any motor output that isn't the awning".
//
// node_type is the page-2 identity byte (device class) for the node, or 0 if not
// yet observed. The static denylist covers the not-yet-observed window.
#include <cstdint>
namespace onecontrol {
inline bool command_blocked(uint8_t node, uint8_t node_type) {
// Known slide / jack / movement-class nodes on this rig — always refused.
if (node == 0x6A || node == 0x7F || node == 0x9C) return true;
// Any movement-class (0x21) node except the awning (0x75).
if (node_type == 0x21 && node != 0x75) return true;
return false;
}
} // namespace onecontrol