hat.drivers.upnp.description

WIP UPnP description

  1"""WIP UPnP description"""
  2
  3import collections
  4import io
  5import typing
  6import xml.sax.handler
  7
  8import aiohttp
  9
 10
 11class Icon(typing.NamedTuple):
 12    mimetype: str
 13    width: int
 14    height: int
 15    depth: int
 16    url: str
 17
 18
 19class DeviceDescription(typing.NamedTuple):
 20    url: str | None
 21    dev_type: str
 22    dev_name: str
 23    manufacturer: str
 24    manufacturer_url: str | None
 25    model_desc: str | None
 26    model_name: str
 27    model_number: str | None
 28    model_url: str | None
 29    serial_number: str | None
 30    unique_dev_name: str
 31    icons: list[Icon]
 32
 33
 34async def get_description(location: str) -> DeviceDescription:
 35    async with aiohttp.ClientSession() as session:
 36        async with session.get(location) as res:
 37            text = await res.text()
 38        data = _decode_xml(text)
 39        version = data['root']['specVersion']
 40        if version['major'] != '1':
 41            raise Exception(f"unsupported version {version}")
 42        device = data['root']['device']
 43        device_icons = device.get('iconList', {}).get('icon', [])
 44        if not isinstance(device_icons, list):
 45            device_icons = [device_icons]
 46        icons = []
 47        for i in device_icons:
 48            icons.append(Icon(mimetype=i['mimetype'],
 49                              width=i['width'],
 50                              height=i['height'],
 51                              depth=i['depth'],
 52                              url=i['url']))
 53        return DeviceDescription(
 54            url=data['root'].get('URLBase'),
 55            dev_type=device['deviceType'],
 56            dev_name=device['friendlyName'],
 57            manufacturer=device['deviceType'],
 58            manufacturer_url=device.get('manufacturerURL'),
 59            model_desc=device.get('modelDescription'),
 60            model_name=device['modelName'],
 61            model_number=device.get('modelNumber'),
 62            model_url=device.get('modelURL'),
 63            serial_number=device.get('serialNumber'),
 64            unique_dev_name=device['UDN'],
 65            icons=icons)
 66
 67
 68def _decode_xml(text):
 69    handler = _XmlContentHandler()
 70    parser = xml.sax.make_parser()
 71    parser.setContentHandler(handler)
 72    parser.setFeature(xml.sax.handler.feature_external_ges, False)
 73    parser.setFeature(xml.sax.handler.feature_external_pes, False)
 74    parser.parse(io.StringIO(text))
 75    return handler.root
 76
 77
 78class _XmlContentHandler(xml.sax.ContentHandler):
 79
 80    def __init__(self):
 81        self._root = {}
 82        self._stack = collections.deque([self._root])
 83
 84    @property
 85    def root(self):
 86        return self._root
 87
 88    def startElement(self, name, attrs):
 89        self._stack.append({})
 90
 91    def endElement(self, name):
 92        el = self._stack.pop()
 93        text = el.get('', '')
 94        children = {k: v for k, v in el.items() if k}
 95        top = self._stack[-1]
 96        if name in top:
 97            if not isinstance(top[name], list):
 98                top[name] = [top[name]]
 99            top[name].append(children or text)
100        else:
101            top[name] = children or text
102
103    def characters(self, content):
104        el = self._stack[-1]
105        el[''] = el.get('', '') + content
class Icon(typing.NamedTuple):
12class Icon(typing.NamedTuple):
13    mimetype: str
14    width: int
15    height: int
16    depth: int
17    url: str

Icon(mimetype, width, height, depth, url)

Icon(mimetype: str, width: int, height: int, depth: int, url: str)

Create new instance of Icon(mimetype, width, height, depth, url)

mimetype: str

Alias for field number 0

width: int

Alias for field number 1

height: int

Alias for field number 2

depth: int

Alias for field number 3

url: str

Alias for field number 4

class DeviceDescription(typing.NamedTuple):
20class DeviceDescription(typing.NamedTuple):
21    url: str | None
22    dev_type: str
23    dev_name: str
24    manufacturer: str
25    manufacturer_url: str | None
26    model_desc: str | None
27    model_name: str
28    model_number: str | None
29    model_url: str | None
30    serial_number: str | None
31    unique_dev_name: str
32    icons: list[Icon]

DeviceDescription(url, dev_type, dev_name, manufacturer, manufacturer_url, model_desc, model_name, model_number, model_url, serial_number, unique_dev_name, icons)

DeviceDescription( url: str | None, dev_type: str, dev_name: str, manufacturer: str, manufacturer_url: str | None, model_desc: str | None, model_name: str, model_number: str | None, model_url: str | None, serial_number: str | None, unique_dev_name: str, icons: list[Icon])

Create new instance of DeviceDescription(url, dev_type, dev_name, manufacturer, manufacturer_url, model_desc, model_name, model_number, model_url, serial_number, unique_dev_name, icons)

url: str | None

Alias for field number 0

dev_type: str

Alias for field number 1

dev_name: str

Alias for field number 2

manufacturer: str

Alias for field number 3

manufacturer_url: str | None

Alias for field number 4

model_desc: str | None

Alias for field number 5

model_name: str

Alias for field number 6

model_number: str | None

Alias for field number 7

model_url: str | None

Alias for field number 8

serial_number: str | None

Alias for field number 9

unique_dev_name: str

Alias for field number 10

icons: list[Icon]

Alias for field number 11

async def get_description(location: str) -> DeviceDescription:
35async def get_description(location: str) -> DeviceDescription:
36    async with aiohttp.ClientSession() as session:
37        async with session.get(location) as res:
38            text = await res.text()
39        data = _decode_xml(text)
40        version = data['root']['specVersion']
41        if version['major'] != '1':
42            raise Exception(f"unsupported version {version}")
43        device = data['root']['device']
44        device_icons = device.get('iconList', {}).get('icon', [])
45        if not isinstance(device_icons, list):
46            device_icons = [device_icons]
47        icons = []
48        for i in device_icons:
49            icons.append(Icon(mimetype=i['mimetype'],
50                              width=i['width'],
51                              height=i['height'],
52                              depth=i['depth'],
53                              url=i['url']))
54        return DeviceDescription(
55            url=data['root'].get('URLBase'),
56            dev_type=device['deviceType'],
57            dev_name=device['friendlyName'],
58            manufacturer=device['deviceType'],
59            manufacturer_url=device.get('manufacturerURL'),
60            model_desc=device.get('modelDescription'),
61            model_name=device['modelName'],
62            model_number=device.get('modelNumber'),
63            model_url=device.get('modelURL'),
64            serial_number=device.get('serialNumber'),
65            unique_dev_name=device['UDN'],
66            icons=icons)