# 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 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 relative <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 ', '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 ', '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 ', '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 ' '', '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 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 ', '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 ' '', '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 <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 ', '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 ', '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 ', '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 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 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 ', '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 ', '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 [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 ', '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 ', '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 ', '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 ', '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)