adv_parser.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. """Library to handle connection with Switchbot."""
  2. from __future__ import annotations
  3. import logging
  4. from collections.abc import Callable
  5. from typing import TypedDict
  6. from bleak.backends.device import BLEDevice
  7. from bleak.backends.scanner import AdvertisementData
  8. from .adv_parsers.bot import process_wohand
  9. from .adv_parsers.bulb import process_color_bulb
  10. from .adv_parsers.ceiling_light import process_woceiling
  11. from .adv_parsers.contact import process_wocontact
  12. from .adv_parsers.curtain import process_wocurtain
  13. from .adv_parsers.humidifier import process_wohumidifier
  14. from .adv_parsers.light_strip import process_wostrip
  15. from .adv_parsers.meter import process_wosensorth
  16. from .adv_parsers.motion import process_wopresence
  17. from .adv_parsers.plug import process_woplugmini
  18. from .const import SwitchbotModel
  19. from .models import SwitchBotAdvertisement
  20. _LOGGER = logging.getLogger(__name__)
  21. class SwitchbotSupportedType(TypedDict):
  22. """Supported type of Switchbot."""
  23. modelName: SwitchbotModel
  24. modelFriendlyName: str
  25. func: Callable[[bytes, bytes | None], dict[str, bool | int]]
  26. SUPPORTED_TYPES: dict[str, SwitchbotSupportedType] = {
  27. "d": {
  28. "modelName": SwitchbotModel.CONTACT_SENSOR,
  29. "modelFriendlyName": "Contact Sensor",
  30. "func": process_wocontact,
  31. },
  32. "H": {
  33. "modelName": SwitchbotModel.BOT,
  34. "modelFriendlyName": "Bot",
  35. "func": process_wohand,
  36. },
  37. "s": {
  38. "modelName": SwitchbotModel.MOTION_SENSOR,
  39. "modelFriendlyName": "Motion Sensor",
  40. "func": process_wopresence,
  41. },
  42. "r": {
  43. "modelName": SwitchbotModel.LIGHT_STRIP,
  44. "modelFriendlyName": "Light Strip",
  45. "func": process_wostrip,
  46. },
  47. "c": {
  48. "modelName": SwitchbotModel.CURTAIN,
  49. "modelFriendlyName": "Curtain",
  50. "func": process_wocurtain,
  51. },
  52. "T": {
  53. "modelName": SwitchbotModel.METER,
  54. "modelFriendlyName": "Meter",
  55. "func": process_wosensorth,
  56. },
  57. "i": {
  58. "modelName": SwitchbotModel.METER,
  59. "modelFriendlyName": "Meter Plus",
  60. "func": process_wosensorth,
  61. },
  62. "g": {
  63. "modelName": SwitchbotModel.PLUG_MINI,
  64. "modelFriendlyName": "Plug Mini",
  65. "func": process_woplugmini,
  66. },
  67. "u": {
  68. "modelName": SwitchbotModel.COLOR_BULB,
  69. "modelFriendlyName": "Color Bulb",
  70. "func": process_color_bulb,
  71. },
  72. "q": {
  73. "modelName": SwitchbotModel.CEILING_LIGHT,
  74. "modelFriendlyName": "Ceiling Light",
  75. "func": process_woceiling,
  76. },
  77. "e": {
  78. "modelName": SwitchbotModel.HUMIDIFIER,
  79. "modelFriendlyName": "Humidifier",
  80. "func": process_wohumidifier,
  81. },
  82. }
  83. def parse_advertisement_data(
  84. device: BLEDevice, advertisement_data: AdvertisementData
  85. ) -> SwitchBotAdvertisement | None:
  86. """Parse advertisement data."""
  87. _services = list(advertisement_data.service_data.values())
  88. _mgr_datas = list(advertisement_data.manufacturer_data.values())
  89. if not _services:
  90. return None
  91. _service_data = _services[0]
  92. if not _service_data:
  93. return None
  94. _mfr_data = _mgr_datas[0] if _mgr_datas else None
  95. _model = chr(_service_data[0] & 0b01111111)
  96. data = {
  97. "address": device.address, # MacOS uses UUIDs
  98. "rawAdvData": list(advertisement_data.service_data.values())[0],
  99. "data": {},
  100. "model": _model,
  101. "isEncrypted": bool(_service_data[0] & 0b10000000),
  102. }
  103. type_data = SUPPORTED_TYPES.get(_model)
  104. if type_data:
  105. data.update(
  106. {
  107. "modelFriendlyName": type_data["modelFriendlyName"],
  108. "modelName": type_data["modelName"],
  109. "data": type_data["func"](_service_data, _mfr_data),
  110. }
  111. )
  112. data["data"]["rssi"] = device.rssi
  113. return SwitchBotAdvertisement(device.address, data, device)