1from dataclasses import dataclass, field
2from typing import Tuple
3
4
5@dataclass(init=False)
6class Address:
7    address: bytes = field(default=bytes([0, 0, 0, 0, 0, 0]))
8
9    def __init__(self, address=None):
10        if not address:
11            self.address = bytes([0, 0, 0, 0, 0, 0])
12        elif isinstance(address, Address):
13            self.address = address.address
14        elif isinstance(address, str):
15            self.address = bytes([int(b, 16) for b in address.split(':')])
16        elif isinstance(address, bytes) and len(address) == 6:
17            self.address = address
18        elif isinstance(address, bytes):
19            address = address.decode('utf-8')
20            self.address = bytes([int(b, 16) for b in address.split(':')])
21        else:
22            raise Exception(f'unsupported address type: {address}')
23
24    def from_str(address: str) -> 'Address':
25        return Address(bytes([int(b, 16) for b in address.split(':')]))
26
27    def parse(span: bytes) -> Tuple['Address', bytes]:
28        assert len(span) >= 6
29        return (Address(bytes(reversed(span[:6]))), span[6:])
30
31    def parse_all(span: bytes) -> 'Address':
32        assert (len(span) == 6)
33        return Address(bytes(reversed(span)))
34
35    def serialize(self) -> bytes:
36        return bytes(reversed(self.address))
37
38    def __repr__(self) -> str:
39        return ':'.join([f'{b:02x}' for b in self.address])
40
41    @property
42    def size(self) -> int:
43        return 6
44
45
46@dataclass(init=False)
47class ClassOfDevice:
48    class_of_device: int = 0
49
50    def __init__(self, class_of_device=None):
51        if not class_of_device:
52            self.class_of_device = 0
53        elif isinstance(class_of_device, int):
54            self.class_of_device = class_of_device
55        elif isinstance(class_of_device, bytes):
56            self.class_of_device = int.from_bytes(class_of_device, byteorder='little')
57        else:
58            raise Exception(f'unsupported class of device type: {class_of_device}')
59
60    def parse(span: bytes) -> Tuple['ClassOfDevice', bytes]:
61        assert len(span) >= 3
62        return (ClassOfDevice(span[:3]), span[3:])
63
64    def parse_all(span: bytes) -> 'ClassOfDevice':
65        assert len(span) == 3
66        return ClassOfDevice(span)
67
68    def serialize(self) -> bytes:
69        return int.to_bytes(self.class_of_device, length=3, byteorder='little')
70
71    def __repr__(self) -> str:
72        return f'{self.class_of_device:06x}'
73
74    @property
75    def size(self) -> int:
76        return 3
77