Files
Cognitive_Agent/src/cognitive_agent/generated/can_semantic_protocol.py
T

865 lines
45 KiB
Python

# Generated by tools/generate_can_semantic_protocol.py; do not edit.
"""Generated Python view of the shared CAN semantic protocol."""
from __future__ import annotations
from typing import Any, Iterable
PROTOCOL_SOURCE_SHA256 = "b597439d69fb292c0aa22c9db383a74f426c945cf0cca8ae492dde3e4d848051"
PROTOCOL_MANIFEST: dict[str, Any] = {'schema': 'cognitive-agent-can-semantic-protocol/v1',
'version': '1.0.1',
'description': 'Single source of truth for CAN0/CAN1 semantic commands and transport metadata. '
'Frame encoders and feedback algorithms remain named special handlers when they '
'require stateful closed-loop behavior.',
'channels': {'0': {'name': 'passenger_seat_can', 'frame_format': 'classic_can', 'bitrate': 500000},
'1': {'name': 'vehicle_body_can',
'frame_format': 'mixed',
'arbitration_bitrate': 500000,
'data_bitrate': 2000000}},
'rules': [{'id': 'can0.seat.position',
'channel': 0,
'domain': 'seat',
'capabilities': ['seat.set_longitudinal_position',
'seat.set_cushion_tilt',
'seat.set_backrest_recline',
'seat.set_legrest_height'],
'help': 'seat passenger <horizontal|basin|backrest|legrest> position <0-100>',
'patterns': [[{'literal': 'seat'},
{'literal': 'passenger'},
{'enum': 'axis',
'values': ['horizontal',
'longitudinal',
'basin',
'height',
'backrest',
'legrest']},
{'literal': 'position'},
{'integer': 'position_pct', 'minimum': 0, 'maximum': 100}]],
'frames': [{'key': 'move',
'frame_id': '0x300',
'format': 'classic_can',
'brs': False,
'dlc': 8,
'template_kind': 'can0_motion_fields',
'base_payload_hex': '00 10 00 00 00 00 00 00',
'fields': {'opcode': {'start_bit': 0,
'bit_length': 8,
'absolute': 1,
'home_zero': 11},
'motor': {'start_bit': 8,
'bit_length': 2,
'values': {'horizontal': 0,
'longitudinal': 0,
'basin': 1,
'height': 1,
'backrest': 2,
'legrest': 3}},
'protocol_version': {'start_bit': 12,
'bit_length': 2,
'constant': 1},
'sequence': {'start_bit': 16,
'bit_length': 16,
'runtime_allocated': True},
'param_a': {'start_bit': 32,
'bit_length': 16,
'scale': 100,
'home_key': 19282},
'param_b': {'start_bit': 48,
'bit_length': 16,
'absolute_timeout_ms': 30000,
'home_timeout_s': 120}},
'repeat': {'kind': 'fixed', 'count': 1, 'period_ms': 0},
'feedback_ids': ['0x301', '0x302'],
'feedback_policy': 'ack_then_motor_terminal'}],
'special_handler': 'can0_absolute_position',
'evidence': 'passenger_firmware_hil_verified'},
{'id': 'can0.seat.relative',
'channel': 0,
'domain': 'seat',
'capabilities': ['seat.set_longitudinal_position',
'seat.set_cushion_tilt',
'seat.set_backrest_recline',
'seat.set_legrest_height'],
'help': 'seat passenger <axis> relative <direction> <1-100>',
'patterns': [[{'literal': 'seat'},
{'literal': 'passenger'},
{'enum': 'axis', 'values': ['horizontal', 'longitudinal']},
{'literal': 'relative'},
{'enum': 'direction', 'values': ['forward', 'backward']},
{'integer': 'delta_pct', 'minimum': 1, 'maximum': 100}],
[{'literal': 'seat'},
{'literal': 'passenger'},
{'enum': 'axis', 'values': ['basin', 'height']},
{'literal': 'relative'},
{'enum': 'direction', 'values': ['up', 'down']},
{'integer': 'delta_pct', 'minimum': 1, 'maximum': 100}],
[{'literal': 'seat'},
{'literal': 'passenger'},
{'literal': 'backrest'},
{'literal': 'relative'},
{'enum': 'direction', 'values': ['forward', 'backward']},
{'integer': 'delta_pct', 'minimum': 1, 'maximum': 100}],
[{'literal': 'seat'},
{'literal': 'passenger'},
{'literal': 'legrest'},
{'literal': 'relative'},
{'enum': 'direction', 'values': ['up', 'down']},
{'integer': 'delta_pct', 'minimum': 1, 'maximum': 100}]],
'frames': [{'key': 'move',
'frame_id': '0x300',
'format': 'classic_can',
'brs': False,
'dlc': 8,
'template_kind': 'can0_relative_closed_loop_or_timed_fallback',
'base_payload_hex': '03 10 00 00 00 00 00 00',
'fields': {'closed_loop_opcode': 1,
'timed_fallback_opcode': 3,
'timed_fallback_max_delta_pct': 15,
'direction_values': {'increase': 1, 'decrease': 2}},
'repeat': {'kind': 'fixed', 'count': 1, 'period_ms': 0},
'feedback_ids': ['0x301', '0x302'],
'feedback_policy': 'fresh_position_then_ack_then_motor_terminal'}],
'special_handler': 'can0_relative_position',
'evidence': 'passenger_firmware_hil_verified'},
{'id': 'can0.seat.thermal',
'channel': 0,
'domain': 'seat',
'capabilities': ['seat.set_ventilation', 'seat.set_heating'],
'help': 'seat passenger <vent|heat> <off|low|medium|high>',
'patterns': [[{'literal': 'seat'},
{'literal': 'passenger'},
{'enum': 'feature', 'values': ['vent', 'heat']},
{'enum': 'level', 'values': ['off', 'low', 'medium', 'high']}]],
'frames': [{'key': 'thermal_mode',
'frame_id': '0x3CE',
'format': 'classic_can',
'brs': False,
'dlc': 8,
'template_kind': 'can0_heating_mode_optional',
'base_payload_hex': '10 00 00 00 00 00 00 00',
'repeat': {'kind': 'fixed', 'count': 1, 'period_ms': 0},
'feedback_ids': ['0x4FA'],
'optional_when': 'heating_on'},
{'key': 'thermal_control',
'frame_id': '0x29E',
'format': 'classic_can',
'brs': False,
'dlc': 8,
'template_kind': 'can0_thermal_bit_fields',
'base_payload_hex': '00 00 00 00 00 00 00 00',
'repeat': {'kind': 'fixed', 'count': 1, 'period_ms': 0},
'feedback_ids': ['0x4FA'],
'feedback_policy': 'thermal_state_match'}],
'special_handler': 'can0_thermal_preserve_peer_feature',
'evidence': 'passenger_firmware_hil_verified'},
{'id': 'can0.seat.dynamic',
'channel': 0,
'domain': 'seat',
'capabilities': ['seat.start_dynamic_mode', 'seat.stop_dynamic_mode'],
'help': 'seat passenger dynamic <rocking-horse|cradle|stop>',
'patterns': [[{'literal': 'seat'},
{'literal': 'passenger'},
{'literal': 'dynamic'},
{'enum': 'mode', 'values': ['rocking-horse', 'cradle', 'stop']}],
[{'literal': 'seat'},
{'literal': 'passenger'},
{'literal': 'dynamic'},
{'literal': 'start'},
{'enum': 'mode', 'values': ['rocking-horse', 'cradle']}]],
'frames': [{'key': 'dynamic',
'frame_id': '0x300',
'format': 'classic_can',
'brs': False,
'dlc': 8,
'template_kind': 'can0_dynamic_fields',
'base_payload_hex': '0E 10 00 00 00 00 00 00',
'fields': {'start_opcode': 14,
'stop_opcode': 15,
'mode_values': {'rocking-horse': 1, 'cradle': 2, 'stop': 0}},
'repeat': {'kind': 'fixed', 'count': 1, 'period_ms': 0},
'feedback_ids': ['0x301', '0x306'],
'feedback_policy': 'ack_then_dynamic_state'}],
'special_handler': 'can0_dynamic_mode',
'evidence': 'passenger_firmware_hil_verified'},
{'id': 'can1.door.rear_lock',
'channel': 1,
'domain': 'door',
'capabilities': ['door.set_rear_lock'],
'help': 'door rear <lock|unlock>',
'patterns': [[{'literal': 'door'},
{'literal': 'rear'},
{'enum': 'state', 'values': ['lock', 'unlock']}]],
'frames': [{'key': 'command',
'frame_id': '0x32B',
'format': 'classic_can',
'brs': False,
'dlc': 8,
'template_kind': 'bit_fields',
'base_payload_hex': '0A 00 00 00 00 00 00 00',
'fields': {'state': {'start_bit': 14,
'bit_length': 2,
'values': {'lock': 1, 'unlock': 2}}},
'repeat': {'kind': 'fixed', 'count': 1, 'period_ms': 0},
'feedback_ids': ['0x407'],
'feedback_policy': 'rear_lock_state_match'}],
'evidence': 'matrix_candidate_with_feedback'},
{'id': 'can1.window.motion',
'channel': 1,
'domain': 'window',
'capabilities': ['window.set_motion'],
'help': 'window <all|front|rear|front-left|front-right|rear-left|rear-right> '
'<open|close|stop>',
'patterns': [[{'literal': 'window'},
{'enum': 'target',
'values': ['all',
'front',
'rear',
'front-left',
'front-right',
'rear-left',
'rear-right']},
{'enum': 'motion', 'values': ['open', 'close', 'stop']}]],
'frames': [{'key': 'command',
'frame_id': '0x431',
'format': 'can_fd',
'brs': True,
'dlc': 8,
'template_kind': 'window_motion_bit_fields',
'base_payload_hex': '00 00 00 00 00 00 00 00',
'fields': {'starts': [48, 56, 51, 59],
'motion_values': {'open': 1, 'close': 2, 'stop': 3}},
'repeat': {'kind': 'fixed', 'count': 3, 'period_ms': 100},
'feedback_ids': ['0x388'],
'feedback_policy': 'window_state_match'}],
'evidence': 'vehicle_trace_verified'},
{'id': 'can1.window.position',
'channel': 1,
'domain': 'window',
'capabilities': ['window.set_position'],
'help': 'window <front-left|front-right|rear-left|rear-right> position <0-100>',
'patterns': [[{'literal': 'window'},
{'enum': 'target',
'values': ['front-left', 'front-right', 'rear-left', 'rear-right']},
{'literal': 'position'},
{'integer': 'position_pct', 'minimum': 0, 'maximum': 100}]],
'frames': [{'key': 'command',
'frame_id': '0x431',
'format': 'can_fd',
'brs': True,
'dlc': 8,
'template_kind': 'window_feedback_closed_loop',
'base_payload_hex': '00 00 00 00 00 00 00 00',
'repeat': {'kind': 'special', 'count': 1, 'period_ms': 0},
'feedback_ids': ['0x388'],
'feedback_policy': 'fresh_position_then_motion_then_stop'}],
'special_handler': 'window_position'},
{'id': 'can1.climate.power',
'channel': 1,
'domain': 'climate',
'capabilities': ['climate.set_power'],
'help': 'ac <on|off>',
'patterns': [[{'literal': 'ac'}, {'enum': 'state', 'values': ['on', 'off']}]],
'frames': [{'key': 'command',
'frame_id': '0x1DE',
'format': 'classic_can',
'brs': False,
'dlc': 8,
'template_kind': 'bit_fields',
'base_payload_hex': '00 00 00 00 00 00 00 00',
'fields': {'state': {'start_bit': 28,
'bit_length': 2,
'values': {'on': 1, 'off': 2}}},
'repeat': {'kind': 'fixed', 'count': 3, 'period_ms': 100},
'feedback_ids': ['0x404'],
'feedback_policy': 'climate_power_match'}],
'evidence': 'matrix_and_vehicle_trace_verified'},
{'id': 'can1.climate.fan',
'channel': 1,
'domain': 'climate',
'capabilities': ['climate.set_fan_level'],
'help': 'ac fan <1-7>',
'patterns': [[{'literal': 'ac'},
{'literal': 'fan'},
{'integer': 'level', 'minimum': 1, 'maximum': 7}]],
'frames': [{'key': 'command',
'frame_id': '0x1DE',
'format': 'classic_can',
'brs': False,
'dlc': 8,
'template_kind': 'bit_fields',
'base_payload_hex': '00 00 00 00 00 00 00 00',
'fields': {'level': {'start_bit': 4, 'bit_length': 4}},
'repeat': {'kind': 'fixed', 'count': 3, 'period_ms': 100},
'feedback_ids': ['0x404'],
'feedback_policy': 'climate_fan_match'}],
'evidence': 'matrix_and_vehicle_trace_verified'},
{'id': 'can1.climate.mode',
'channel': 1,
'domain': 'climate',
'capabilities': ['climate.set_outlet_mode'],
'help': 'ac mode '
'<face|face-feet|feet|feet-defrost|defrost|face-feet-defrost|face-defrost>',
'patterns': [[{'literal': 'ac'},
{'literal': 'mode'},
{'enum': 'mode',
'values': ['face',
'face-feet',
'feet',
'feet-defrost',
'defrost',
'face-feet-defrost',
'face-defrost']}]],
'frames': [{'key': 'command',
'frame_id': '0x1DE',
'format': 'classic_can',
'brs': False,
'dlc': 8,
'template_kind': 'bit_fields',
'base_payload_hex': '00 00 00 00 00 00 00 00',
'fields': {'mode': {'start_bit': 0,
'bit_length': 4,
'values': {'face': 1,
'face-feet': 2,
'feet': 3,
'feet-defrost': 4,
'defrost': 5,
'face-feet-defrost': 6,
'face-defrost': 7}}},
'repeat': {'kind': 'fixed', 'count': 3, 'period_ms': 100},
'feedback_ids': ['0x404'],
'feedback_policy': 'climate_mode_match'}],
'evidence': 'matrix_and_vehicle_trace_verified'},
{'id': 'can1.climate.temperature',
'channel': 1,
'domain': 'climate',
'capabilities': ['climate.set_temperature'],
'help': 'ac temp <driver|passenger|rear> <18-32>',
'patterns': [[{'literal': 'ac'},
{'literal': 'temp'},
{'enum': 'target', 'values': ['driver', 'passenger', 'rear']},
{'integer': 'temperature_c', 'minimum': 18, 'maximum': 32}]],
'frames': [{'key': 'front',
'frame_id': '0x1DE',
'format': 'classic_can',
'brs': False,
'dlc': 8,
'template_kind': 'climate_temperature',
'base_payload_hex': '00 00 00 00 00 00 00 00',
'fields': {'starts': {'driver': 32, 'passenger': 40}},
'repeat': {'kind': 'fixed', 'count': 3, 'period_ms': 100},
'feedback_ids': ['0x404'],
'feedback_policy': 'climate_temperature_match'},
{'key': 'rear',
'frame_id': '0x1DE',
'format': 'classic_can',
'brs': False,
'dlc': 8,
'template_kind': 'climate_temperature',
'base_payload_hex': '00 00 00 00 00 00 00 00',
'fields': {'starts': {'rear': 48}},
'repeat': {'kind': 'fixed', 'count': 1, 'period_ms': 100},
'feedback_ids': ['0x404'],
'feedback_policy': 'climate_temperature_match'}],
'evidence': 'matrix_and_vehicle_trace_verified'},
{'id': 'can1.climate.control',
'channel': 1,
'domain': 'climate',
'capabilities': ['climate.set_control_mode'],
'help': 'ac control <manual|auto>',
'patterns': [[{'literal': 'ac'},
{'literal': 'control'},
{'enum': 'mode', 'values': ['manual', 'auto']}]],
'frames': [{'key': 'command',
'frame_id': '0x1DE',
'format': 'classic_can',
'brs': False,
'dlc': 8,
'template_kind': 'bit_fields',
'base_payload_hex': '00 00 00 00 00 00 00 00',
'fields': {'mode': {'start_bit': 16,
'bit_length': 2,
'values': {'manual': 1, 'auto': 2}}},
'repeat': {'kind': 'fixed', 'count': 3, 'period_ms': 100},
'feedback_ids': ['0x404'],
'feedback_policy': 'climate_control_match'}],
'evidence': 'matrix_and_vehicle_trace_verified'},
{'id': 'can1.climate.cycle',
'channel': 1,
'domain': 'climate',
'capabilities': ['climate.set_cycle_mode'],
'help': 'ac cycle <internal|external>',
'patterns': [[{'literal': 'ac'},
{'literal': 'cycle'},
{'enum': 'mode', 'values': ['internal', 'external']}]],
'frames': [{'key': 'command',
'frame_id': '0x1DE',
'format': 'classic_can',
'brs': False,
'dlc': 8,
'template_kind': 'bit_fields',
'base_payload_hex': '00 00 00 00 00 00 00 00',
'fields': {'mode': {'start_bit': 19,
'bit_length': 2,
'values': {'internal': 1, 'external': 2}}},
'repeat': {'kind': 'fixed', 'count': 3, 'period_ms': 100},
'feedback_ids': ['0x404'],
'feedback_policy': 'climate_cycle_match'}],
'evidence': 'matrix_and_vehicle_trace_verified'},
{'id': 'can1.outlet.switch',
'channel': 1,
'domain': 'outlet',
'capabilities': ['outlet.set_switch_state'],
'help': 'outlet <driver-left|driver-right|passenger-left|passenger-right> <open|close>',
'patterns': [[{'literal': 'outlet'},
{'enum': 'target',
'values': ['driver-left',
'driver-right',
'passenger-left',
'passenger-right']},
{'enum': 'state', 'values': ['open', 'close']}]],
'frames': [{'key': 'command',
'frame_id': '0x4EF',
'format': 'can_fd',
'brs': True,
'dlc': 8,
'template_kind': 'static_variant',
'base_payload_hex': '00 00 00 00 00 00 00 00',
'payload_variants': {'driver-left:open': '02 FF FF FF FF 01 FF FF',
'driver-left:close': '02 FF FF FF FF 02 FF FF',
'driver-right:open': '02 FF FF FF FF 04 FF FF',
'driver-right:close': '02 FF FF FF FF 08 FF FF',
'passenger-left:open': '03 FF FF FF FF 01 00 00',
'passenger-left:close': '03 FF FF FF FF 02 00 00',
'passenger-right:open': '03 FF FF FF FF 04 00 00',
'passenger-right:close': '03 FF FF FF FF 08 00 00'},
'repeat': {'kind': 'fixed', 'count': 3, 'period_ms': 100},
'feedback_ids': ['0x4BB', '0x37B'],
'feedback_policy': 'outlet_switch_state_match'}],
'evidence': 'vehicle_differential_trace_verified'},
{'id': 'can1.outlet.percent',
'channel': 1,
'domain': 'outlet',
'capabilities': ['outlet.set_position_percent'],
'help': 'outlet <target> percent <0-100> <0-100>',
'patterns': [[{'literal': 'outlet'},
{'enum': 'target',
'values': ['driver-left',
'driver-right',
'passenger-left',
'passenger-right']},
{'literal': 'percent'},
{'integer': 'horizontal_pct', 'minimum': 0, 'maximum': 100},
{'integer': 'vertical_pct', 'minimum': 0, 'maximum': 100}]],
'frames': [{'key': 'command',
'frame_id': '0x4EF',
'format': 'can_fd',
'brs': True,
'dlc': 8,
'template_kind': 'outlet_calibrated_coordinates',
'base_payload_hex': '00 FF FF FF FF 00 00 00',
'repeat': {'kind': 'fixed', 'count': 3, 'period_ms': 100},
'feedback_ids': ['0x2F5', '0x37B'],
'feedback_policy': 'outlet_position_match'}],
'special_handler': 'outlet_percent_to_calibrated_position'},
{'id': 'can1.outlet.position',
'channel': 1,
'domain': 'outlet',
'capabilities': ['outlet.set_calibrated_position'],
'help': 'outlet <target> position <-128..126> <-128..126>',
'patterns': [[{'literal': 'outlet'},
{'enum': 'target',
'values': ['driver-left',
'driver-right',
'passenger-left',
'passenger-right']},
{'literal': 'position'},
{'integer': 'horizontal_value', 'minimum': -128, 'maximum': 126},
{'integer': 'vertical_value', 'minimum': -128, 'maximum': 126}]],
'frames': [{'key': 'command',
'frame_id': '0x4EF',
'format': 'can_fd',
'brs': True,
'dlc': 8,
'template_kind': 'outlet_calibrated_coordinates',
'base_payload_hex': '00 FF FF FF FF 00 00 00',
'repeat': {'kind': 'fixed', 'count': 3, 'period_ms': 100},
'feedback_ids': ['0x2F5', '0x37B'],
'feedback_policy': 'outlet_position_match'}],
'special_handler': 'outlet_calibrated_position'},
{'id': 'can1.seat.driver_thermal',
'channel': 1,
'domain': 'seat',
'capabilities': ['seat.set_driver_ventilation_native',
'seat.set_driver_heating_native'],
'help': 'seat driver <vent|heat> <off|low|high>',
'patterns': [[{'literal': 'seat'},
{'literal': 'driver'},
{'enum': 'feature', 'values': ['vent', 'heat']},
{'enum': 'level', 'values': ['off', 'low', 'high']}]],
'frames': [{'key': 'command',
'frame_id': '0x431',
'format': 'can_fd',
'brs': True,
'dlc': 8,
'template_kind': 'driver_seat_thermal',
'base_payload_hex': '00 00 00 00 00 00 00 00',
'repeat': {'kind': 'fixed', 'count': 3, 'period_ms': 100},
'feedback_ids': ['0x4FA'],
'feedback_policy': 'driver_seat_thermal_match'}],
'evidence': 'vehicle_trace_verified'},
{'id': 'can1.seat.driver_massage',
'channel': 1,
'domain': 'seat',
'capabilities': ['seat.set_driver_massage_native'],
'help': 'seat driver massage <off|strength weak|medium|strong|mode '
'wave|pulse|stretch|slack|relax>',
'patterns': [[{'literal': 'seat'},
{'literal': 'driver'},
{'literal': 'massage'},
{'literal': 'off'}],
[{'literal': 'seat'},
{'literal': 'driver'},
{'literal': 'massage'},
{'literal': 'strength'},
{'enum': 'strength', 'values': ['weak', 'medium', 'strong']}],
[{'literal': 'seat'},
{'literal': 'driver'},
{'literal': 'massage'},
{'literal': 'mode'},
{'enum': 'mode',
'values': ['wave', 'pulse', 'stretch', 'slack', 'relax']}]],
'frames': [{'key': 'command',
'frame_id': '0x4C1',
'format': 'can_fd',
'brs': True,
'dlc': 8,
'template_kind': 'driver_seat_massage',
'base_payload_hex': '12 00 00 00 00 00 00 00',
'repeat': {'kind': 'fixed', 'count': 3, 'period_ms': 100},
'feedback_ids': ['0x4FA'],
'feedback_policy': 'driver_seat_massage_match'}],
'evidence': 'vehicle_trace_verified'},
{'id': 'can1.seat.driver_axis',
'channel': 1,
'domain': 'seat',
'capabilities': ['seat.set_driver_axis_native'],
'help': 'seat driver <horizontal|height|backrest|basin> <direction|stop> [100-2000]',
'patterns': [[{'literal': 'seat'},
{'literal': 'driver'},
{'enum': 'axis', 'values': ['horizontal', 'backrest']},
{'enum': 'direction', 'values': ['forward', 'backward']}],
[{'literal': 'seat'},
{'literal': 'driver'},
{'enum': 'axis', 'values': ['horizontal', 'backrest']},
{'enum': 'direction', 'values': ['forward', 'backward']},
{'integer': 'duration_ms', 'minimum': 100, 'maximum': 2000}],
[{'literal': 'seat'},
{'literal': 'driver'},
{'enum': 'axis', 'values': ['height', 'basin']},
{'enum': 'direction', 'values': ['up', 'down']}],
[{'literal': 'seat'},
{'literal': 'driver'},
{'enum': 'axis', 'values': ['height', 'basin']},
{'enum': 'direction', 'values': ['up', 'down']},
{'integer': 'duration_ms', 'minimum': 100, 'maximum': 2000}],
[{'literal': 'seat'},
{'literal': 'driver'},
{'enum': 'axis', 'values': ['horizontal', 'height', 'backrest', 'basin']},
{'literal': 'stop'}]],
'frames': [{'key': 'motion',
'frame_id': '0x4C1',
'format': 'can_fd',
'brs': True,
'dlc': 8,
'template_kind': 'driver_seat_axis',
'base_payload_hex': '05 00 00 00 00 00 00 00',
'repeat': {'kind': 'duration_ceiling',
'count': 0,
'period_ms': 100,
'default_duration_ms': 500},
'feedback_ids': ['0x494'],
'feedback_policy': 'activity_then_forced_stop'},
{'key': 'stop',
'frame_id': '0x4C1',
'format': 'can_fd',
'brs': True,
'dlc': 8,
'template_kind': 'static',
'base_payload_hex': '05 00 00 00 00 00 00 00',
'repeat': {'kind': 'fixed', 'count': 3, 'period_ms': 100},
'feedback_ids': ['0x494'],
'feedback_policy': 'submitted_stop'}],
'special_handler': 'driver_seat_axis'},
{'id': 'can1.seat.driver_memory',
'channel': 1,
'domain': 'seat',
'capabilities': ['seat.set_driver_position_native', 'seat.recall_driver_memory_native'],
'help': 'seat driver position <1-3> | seat driver memory recall <1-3>',
'patterns': [[{'literal': 'seat'},
{'literal': 'driver'},
{'literal': 'position'},
{'integer': 'slot', 'minimum': 1, 'maximum': 3}],
[{'literal': 'seat'},
{'literal': 'driver'},
{'literal': 'memory'},
{'literal': 'recall'},
{'integer': 'slot', 'minimum': 1, 'maximum': 3}]],
'frames': [{'key': 'sequence',
'frame_id': '0x4C1',
'format': 'can_fd',
'brs': True,
'dlc': 8,
'template_kind': 'learned_driver_memory_pair',
'base_payload_hex': '16 00 00 00 00 00 00 00',
'repeat': {'kind': 'sequence', 'count': 20, 'period_ms': 50},
'feedback_ids': ['0x494'],
'feedback_policy': 'fresh_initialized_status_then_post_status'}],
'special_handler': 'driver_seat_memory_recall'},
{'id': 'can1.light.indoor',
'channel': 1,
'domain': 'light',
'capabilities': ['interior_light.set_indoor_power'],
'help': 'light indoor <on|off>',
'patterns': [[{'literal': 'light'},
{'literal': 'indoor'},
{'enum': 'state', 'values': ['on', 'off']}]],
'frames': [{'key': 'command',
'frame_id': '0x4F5',
'format': 'can_fd',
'brs': True,
'dlc': 8,
'template_kind': 'reading_light_all',
'base_payload_hex': '00 00 03 FF FF 00 00 00',
'repeat': {'kind': 'fixed', 'count': 3, 'period_ms': 100},
'feedback_ids': ['0x42E'],
'feedback_policy': 'indoor_light_match'}],
'evidence': 'vehicle_trace_verified'},
{'id': 'can1.light.reading_all',
'channel': 1,
'domain': 'light',
'capabilities': ['interior_light.set_reading_power'],
'help': 'light reading all <on|off>',
'patterns': [[{'literal': 'light'},
{'literal': 'reading'},
{'literal': 'all'},
{'enum': 'state', 'values': ['on', 'off']}]],
'frames': [{'key': 'command',
'frame_id': '0x4F5',
'format': 'can_fd',
'brs': True,
'dlc': 8,
'template_kind': 'reading_light_all',
'base_payload_hex': '00 00 03 FF FF 00 00 00',
'repeat': {'kind': 'fixed', 'count': 3, 'period_ms': 100},
'feedback_ids': ['0x42E'],
'feedback_policy': 'indoor_light_match'}],
'evidence': 'vehicle_trace_verified'},
{'id': 'can1.light.ambient_power',
'channel': 1,
'domain': 'light',
'capabilities': ['ambient_light.set_power'],
'help': 'light ambient <on|off>',
'patterns': [[{'literal': 'light'},
{'literal': 'ambient'},
{'enum': 'state', 'values': ['on', 'off']}]],
'frames': [{'key': 'command',
'frame_id': '0x3FC',
'format': 'can_fd',
'brs': True,
'dlc': 8,
'template_kind': 'static_variant',
'base_payload_hex': '00 00 00 00 00 00 00 00',
'payload_variants': {'on': '00 36 00 75 00 80 00 00',
'off': '00 31 00 75 00 80 00 00'},
'repeat': {'kind': 'fixed', 'count': 3, 'period_ms': 100},
'feedback_ids': ['0x42E'],
'feedback_policy': 'ambient_power_match'}],
'evidence': 'vehicle_trace_verified'},
{'id': 'can1.light.ambient_color',
'channel': 1,
'domain': 'light',
'capabilities': ['ambient_light.set_color_index'],
'help': 'light ambient color <1-127>',
'patterns': [[{'literal': 'light'},
{'literal': 'ambient'},
{'literal': 'color'},
{'integer': 'color_index', 'minimum': 1, 'maximum': 127}]],
'frames': [{'key': 'command',
'frame_id': '0x3FC',
'format': 'can_fd',
'brs': True,
'dlc': 8,
'template_kind': 'ambient_color',
'base_payload_hex': '00 30 00 75 00 00 00 00',
'repeat': {'kind': 'fixed', 'count': 3, 'period_ms': 100},
'feedback_ids': ['0x42E'],
'feedback_policy': 'ambient_color_match'}],
'evidence': 'vehicle_trace_verified'},
{'id': 'can1.light.ambient_brightness',
'channel': 1,
'domain': 'light',
'capabilities': ['ambient_light.set_brightness_level'],
'help': 'light ambient brightness <1-5>',
'patterns': [[{'literal': 'light'},
{'literal': 'ambient'},
{'literal': 'brightness'},
{'integer': 'brightness_level', 'minimum': 1, 'maximum': 5}]],
'frames': [{'key': 'command',
'frame_id': '0x3FC',
'format': 'can_fd',
'brs': True,
'dlc': 8,
'template_kind': 'ambient_brightness',
'base_payload_hex': '00 30 00 75 00 00 00 00',
'repeat': {'kind': 'fixed', 'count': 3, 'period_ms': 100},
'feedback_ids': ['0x42E'],
'feedback_policy': 'ambient_brightness_match'}],
'evidence': 'vehicle_trace_verified'},
{'id': 'can1.shade.motion',
'channel': 1,
'domain': 'roof',
'capabilities': ['roof.set_shade_motion'],
'help': 'shade <open|close|half|stop>',
'patterns': [[{'literal': 'shade'},
{'enum': 'motion', 'values': ['open', 'close', 'half', 'stop']}]],
'frames': [{'key': 'command',
'frame_id': '0x431',
'format': 'classic_can',
'brs': False,
'dlc': 8,
'template_kind': 'static_variant',
'base_payload_hex': '00 00 00 00 00 00 00 00',
'payload_variants': {'open': '10 00 00 00 00 00 00 00',
'close': '20 00 00 00 00 00 00 00',
'half': '30 00 00 00 00 00 00 00',
'stop': '40 00 00 00 00 00 00 00'},
'repeat': {'kind': 'fixed', 'count': 1, 'period_ms': 0},
'feedback_ids': ['0x41A'],
'feedback_policy': 'shade_position_or_stop_submission'}],
'special_handler': 'shade_preset'},
{'id': 'can1.shade.position',
'channel': 1,
'domain': 'roof',
'capabilities': ['roof.set_shade_position'],
'help': 'shade position <0-100>',
'patterns': [[{'literal': 'shade'},
{'literal': 'position'},
{'integer': 'position_pct', 'minimum': 0, 'maximum': 100}]],
'frames': [{'key': 'command',
'frame_id': '0x431',
'format': 'classic_can',
'brs': False,
'dlc': 8,
'template_kind': 'shade_feedback_closed_loop',
'base_payload_hex': '40 00 00 00 00 00 00 00',
'repeat': {'kind': 'special', 'count': 1, 'period_ms': 0},
'feedback_ids': ['0x41A'],
'feedback_policy': 'fresh_position_then_motion_then_stop'}],
'special_handler': 'shade_position'}]}
PROTOCOL_VERSION = str(PROTOCOL_MANIFEST["version"])
RULES: tuple[dict[str, Any], ...] = tuple(PROTOCOL_MANIFEST["rules"])
RULE_BY_ID = {str(rule["id"]): rule for rule in RULES}
CAN0_SUPPORTED_CAPABILITIES = frozenset(
capability
for rule in RULES
if int(rule["channel"]) == 0
for capability in rule["capabilities"]
)
CAN1_SUPPORTED_CAPABILITIES = frozenset(
capability
for rule in RULES
if int(rule["channel"]) == 1
for capability in rule["capabilities"]
)
def rule(rule_id: str) -> dict[str, Any]:
try:
return RULE_BY_ID[rule_id]
except KeyError as exc:
raise KeyError(f"unknown generated CAN protocol rule {rule_id}") from exc
def frame(rule_id: str, key: str = "command") -> dict[str, Any]:
for item in rule(rule_id)["frames"]:
if item["key"] == key:
return item
raise KeyError(f"rule {rule_id} has no frame named {key}")
def command_contract(rule_id: str, key: str = "command") -> dict[str, Any]:
metadata = frame(rule_id, key)
return {
"source_version": PROTOCOL_VERSION,
"rule_id": rule_id,
"frame_key": key,
"frame_id": metadata["frame_id"],
"format": metadata["format"],
"brs": bool(metadata["brs"]),
"dlc": int(metadata["dlc"]),
"repeat": dict(metadata["repeat"]),
"feedback_ids": list(metadata["feedback_ids"]),
"feedback_policy": str(metadata.get("feedback_policy", "")),
}
def payload_variant(rule_id: str, selector: str, key: str = "command") -> bytes:
item = frame(rule_id, key)
try:
value = item["payload_variants"][selector]
except KeyError as exc:
raise KeyError(f"rule {rule_id} frame {key} has no payload {selector}") from exc
return bytes.fromhex(str(value))
def rules_for_capability(capability: str, channel: int | None = None) -> tuple[dict[str, Any], ...]:
return tuple(
item
for item in RULES
if capability in item["capabilities"]
and (channel is None or int(item["channel"]) == channel)
)
def _matches_token(word: str, token: dict[str, Any]) -> bool:
if "literal" in token:
return word == token["literal"]
if "enum" in token:
return word in token["values"]
try:
value = int(word, 10)
except ValueError:
return False
return str(value) == word or (word.startswith("-") and str(value) == word)
def _matches_integer(word: str, token: dict[str, Any]) -> bool:
try:
value = int(word, 10)
except ValueError:
return False
return token["minimum"] <= value <= token["maximum"] and str(value) == word
def match_semantic_command(words: str | Iterable[str], channel: int | None = None) -> dict[str, Any] | None:
normalized = tuple(
part.lower()
for part in (words.split() if isinstance(words, str) else words)
)
for item in RULES:
if channel is not None and int(item["channel"]) != channel:
continue
for pattern in item["patterns"]:
if len(pattern) != len(normalized):
continue
if all(
_matches_integer(word, token) if "integer" in token else _matches_token(word, token)
for word, token in zip(normalized, pattern)
):
return item
return None
def whitelist_help(channel: int) -> tuple[str, ...]:
return tuple(str(item["help"]) for item in RULES if int(item["channel"]) == channel)