import xml.etree.ElementTree as ET
from typing import Dict, List
from .naming import convert_to_snake_case, escape_rust_keyword
from .models import (
MatterEnum,
MatterBitmap,
MatterStruct,
AttributeField,
MatterCommand,
MatterCommandResponse,
MatterField,
MatterEvent,
)
def _parse_field_element(field_elem) -> MatterField:
field_id = int(field_elem.get('id', '0'))
field_name = field_elem.get('name', 'Unknown')
field_type = field_elem.get('type', 'uint8')
field_default = field_elem.get('default')
entry_elem = field_elem.find('entry')
entry_type = entry_elem.get('type') if entry_elem is not None else None
quality_elem = field_elem.find('quality')
nullable = quality_elem.get('nullable', 'false').lower() == 'true' if quality_elem is not None else False
mandatory_elem = field_elem.find('mandatoryConform')
mandatory = mandatory_elem is not None
return MatterField(
id=field_id,
name=field_name,
field_type=field_type,
entry_type=entry_type,
default=field_default,
nullable=nullable,
mandatory=mandatory
)
class ClusterParser:
def __init__(self, xml_file: str):
self.xml_file = xml_file
self.tree = ET.parse(xml_file)
self.root = self.tree.getroot()
self.cluster_name = self.root.get('name', 'Unknown')
self.cluster_id = self.root.get('id', '0x0000')
def parse_commands(self) -> List[MatterCommand]:
commands = []
commands_elem = self.root.find('commands')
if commands_elem is None:
return commands
for cmd_elem in commands_elem.findall('command'):
cmd_id = cmd_elem.get('id', '0x00')
cmd_name = cmd_elem.get('name', 'Unknown')
cmd_direction = cmd_elem.get('direction', 'commandToServer')
if cmd_direction != 'commandToServer':
continue
response_attr = cmd_elem.get('response')
response_name = response_attr if response_attr and response_attr != 'Y' else None
command = MatterCommand(cmd_id, cmd_name, cmd_direction, response_name=response_name)
for field_elem in cmd_elem.findall('field'):
field = _parse_field_element(field_elem)
command.add_field(field)
if not command.fields and cmd_name.endswith('WithOnOff'):
base_name = cmd_name.replace('WithOnOff', '')
for base_cmd_elem in commands_elem.findall('command'):
if base_cmd_elem.get('name') == base_name:
for field_elem in base_cmd_elem.findall('field'):
field = _parse_field_element(field_elem)
command.add_field(field)
break
commands.append(command)
return commands
def parse_response_commands(self) -> List[MatterCommandResponse]:
responses = []
commands_elem = self.root.find('commands')
if commands_elem is None:
return responses
for cmd_elem in commands_elem.findall('command'):
cmd_direction = cmd_elem.get('direction', 'commandToServer')
if cmd_direction != 'responseFromServer':
continue
cmd_id = cmd_elem.get('id', '0x00')
cmd_name = cmd_elem.get('name', 'Unknown')
response = MatterCommandResponse(cmd_id, cmd_name)
for field_elem in cmd_elem.findall('field'):
field = _parse_field_element(field_elem)
response.add_field(field)
responses.append(response)
return responses
def parse_events(self) -> List[MatterEvent]:
events = []
events_elem = self.root.find('events')
if events_elem is None:
return events
for event_elem in events_elem.findall('event'):
event_id = event_elem.get('id', '0x00')
event_name = event_elem.get('name', 'Unknown')
event_priority = event_elem.get('priority', 'info')
event = MatterEvent(event_id, event_name, event_priority)
for field_elem in event_elem.findall('field'):
field = _parse_field_element(field_elem)
event.add_field(field)
events.append(event)
return events
def parse_attributes(self) -> List[AttributeField]:
attributes = []
attributes_elem = self.root.find('attributes')
if attributes_elem is None:
return attributes
for attr_elem in attributes_elem.findall('attribute'):
attr_id = attr_elem.get('id', '0x0000')
attr_name = attr_elem.get('name', 'Unknown')
attr_type = attr_elem.get('type', 'uint8')
attr_default = attr_elem.get('default')
entry_elem = attr_elem.find('entry')
entry_type = None
if entry_elem is not None:
entry_type = entry_elem.get('type')
quality_elem = attr_elem.find('quality')
nullable = False
if quality_elem is not None:
nullable = quality_elem.get('nullable', 'false').lower() == 'true'
field = MatterField(
id=0, name=attr_name,
field_type=attr_type,
entry_type=entry_type,
default=attr_default,
nullable=nullable,
mandatory=True )
attribute = AttributeField(attr_id, field)
attributes.append(attribute)
return attributes
def parse_structs(self) -> Dict[str, MatterStruct]:
structs = {}
data_types_elem = self.root.find('dataTypes')
if data_types_elem is None:
return structs
for struct_elem in data_types_elem.findall('struct'):
struct_name = struct_elem.get('name', 'Unknown')
struct = MatterStruct(struct_name)
for field_elem in struct_elem.findall('field'):
field = _parse_field_element(field_elem)
struct.add_field(field)
structs[struct_name] = struct
return structs
def parse_enums(self) -> Dict[str, MatterEnum]:
enums = {}
data_types_elem = self.root.find('dataTypes')
if data_types_elem is None:
return enums
for enum_elem in data_types_elem.findall('enum'):
enum_name = enum_elem.get('name', 'Unknown')
enum = MatterEnum(enum_name)
for item_elem in enum_elem.findall('item'):
value_str = item_elem.get('value', '0')
item_name = item_elem.get('name', 'Unknown')
summary = item_elem.get('summary', '')
try:
if value_str.startswith('0x') or value_str.startswith('0X'):
value = int(value_str, 16)
else:
value = int(value_str)
except ValueError:
value = 0
enum.add_item(value, item_name, summary)
enums[enum_name] = enum
return enums
def parse_bitmaps(self) -> Dict[str, MatterBitmap]:
bitmaps = {}
data_types_elem = self.root.find('dataTypes')
if data_types_elem is None:
return bitmaps
for bitmap_elem in data_types_elem.findall('bitmap'):
bitmap_name = bitmap_elem.get('name', 'Unknown')
bitmap = MatterBitmap(bitmap_name)
for bitfield_elem in bitmap_elem.findall('bitfield'):
bit_str = bitfield_elem.get('bit')
if bit_str is None:
continue
field_name = bitfield_elem.get('name', 'Unknown')
summary = bitfield_elem.get('summary', '')
try:
if bit_str.startswith('0x') or bit_str.startswith('0X'):
bit_pos = int(bit_str, 16)
else:
bit_pos = int(bit_str)
except (ValueError, AttributeError):
continue
bitmap.add_bitfield(bit_pos, field_name, summary)
bitmaps[bitmap_name] = bitmap
return bitmaps