test_adv_parser.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. from typing import Any
  2. from switchbot.adv_parser import parse_advertisement_data
  3. from bleak.backends.scanner import AdvertisementData
  4. from bleak.backends.device import BLEDevice
  5. from switchbot.models import SwitchBotAdvertisement
  6. from switchbot import SwitchbotModel
  7. ADVERTISEMENT_DATA_DEFAULTS = {
  8. "local_name": "",
  9. "manufacturer_data": {},
  10. "service_data": {},
  11. "service_uuids": [],
  12. "rssi": -127,
  13. "platform_data": ((),),
  14. "tx_power": -127,
  15. }
  16. def generate_advertisement_data(**kwargs: Any) -> AdvertisementData:
  17. """Generate advertisement data with defaults."""
  18. new = kwargs.copy()
  19. for key, value in ADVERTISEMENT_DATA_DEFAULTS.items():
  20. new.setdefault(key, value)
  21. return AdvertisementData(**new)
  22. def test_parse_advertisement_data_curtain():
  23. """Test parse_advertisement_data for curtain."""
  24. ble_device = BLEDevice("aa:bb:cc:dd:ee:ff", "any")
  25. adv_data = generate_advertisement_data(
  26. manufacturer_data={2409: b"\xe7\xabF\xac\x8f\x92|\x0f\x00\x11\x04"},
  27. service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b"c\xc0X\x00\x11\x04"},
  28. rssi=-80,
  29. )
  30. result = parse_advertisement_data(ble_device, adv_data)
  31. assert result == SwitchBotAdvertisement(
  32. address="aa:bb:cc:dd:ee:ff",
  33. data={
  34. "rawAdvData": b"c\xc0X\x00\x11\x04",
  35. "data": {
  36. "calibration": True,
  37. "battery": 88,
  38. "inMotion": False,
  39. "position": 100,
  40. "lightLevel": 1,
  41. "deviceChain": 1,
  42. },
  43. "isEncrypted": False,
  44. "model": "c",
  45. "modelFriendlyName": "Curtain",
  46. "modelName": SwitchbotModel.CURTAIN,
  47. },
  48. device=ble_device,
  49. rssi=-80,
  50. )
  51. def test_parse_advertisement_data_curtain_position_zero():
  52. """Test parse_advertisement_data for curtain position zero."""
  53. ble_device = BLEDevice("aa:bb:cc:dd:ee:ff", "any")
  54. adv_data = generate_advertisement_data(
  55. local_name="WoCurtain",
  56. manufacturer_data={89: b"\xc1\xc7'}U\xab"},
  57. service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"c\xd0\xced\x11\x04"},
  58. service_uuids=[
  59. "00001800-0000-1000-8000-00805f9b34fb",
  60. "00001801-0000-1000-8000-00805f9b34fb",
  61. "cba20d00-224d-11e6-9fb8-0002a5d5c51b",
  62. ],
  63. rssi=-52,
  64. )
  65. result = parse_advertisement_data(ble_device, adv_data)
  66. assert result == SwitchBotAdvertisement(
  67. address="aa:bb:cc:dd:ee:ff",
  68. data={
  69. "rawAdvData": b"c\xd0\xced\x11\x04",
  70. "data": {
  71. "calibration": True,
  72. "battery": 78,
  73. "inMotion": False,
  74. "position": 0,
  75. "lightLevel": 1,
  76. "deviceChain": 1,
  77. },
  78. "isEncrypted": False,
  79. "model": "c",
  80. "modelFriendlyName": "Curtain",
  81. "modelName": SwitchbotModel.CURTAIN,
  82. },
  83. device=ble_device,
  84. rssi=-52,
  85. )
  86. def test_parse_advertisement_data_contact():
  87. """Test parse_advertisement_data for the contact sensor."""
  88. ble_device = BLEDevice("aa:bb:cc:dd:ee:ff", "any")
  89. adv_data = generate_advertisement_data(
  90. manufacturer_data={2409: b"\xe7\xabF\xac\x8f\x92|\x0f\x00\x11\x04"},
  91. service_data={
  92. "0000fd3d-0000-1000-8000-00805f9b34fb": b"d@d\x05\x00u\x00\xf8\x12"
  93. },
  94. rssi=-80,
  95. )
  96. result = parse_advertisement_data(ble_device, adv_data)
  97. assert result == SwitchBotAdvertisement(
  98. address="aa:bb:cc:dd:ee:ff",
  99. data={
  100. "rawAdvData": b"d@d\x05\x00u\x00\xf8\x12",
  101. "data": {
  102. "button_count": 2,
  103. "contact_open": True,
  104. "contact_timeout": True,
  105. "is_light": True,
  106. "battery": 100,
  107. "motion_detected": True,
  108. "tested": False,
  109. },
  110. "isEncrypted": False,
  111. "model": "d",
  112. "modelFriendlyName": "Contact Sensor",
  113. "modelName": SwitchbotModel.CONTACT_SENSOR,
  114. },
  115. device=ble_device,
  116. rssi=-80,
  117. )
  118. def test_parse_advertisement_data_empty():
  119. """Test parse_advertisement_data with empty data does not blow up."""
  120. ble_device = BLEDevice("aa:bb:cc:dd:ee:ff", "any")
  121. adv_data = generate_advertisement_data(
  122. manufacturer_data={2409: b"\xe7\xabF\xac\x8f\x92|\x0f\x00\x11\x04"},
  123. service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": b""},
  124. )
  125. result = parse_advertisement_data(ble_device, adv_data)
  126. assert result is None
  127. def test_new_bot_firmware():
  128. """Test parsing adv data from new bot firmware."""
  129. ble_device = BLEDevice("aa:bb:cc:dd:ee:ff", "any")
  130. adv_data = generate_advertisement_data(
  131. manufacturer_data={89: b"\xd8.\xad\xcd\r\x85"},
  132. service_data={"00000d00-0000-1000-8000-00805f9b34fb": b"H\x10\xe1"},
  133. service_uuids=["CBA20D00-224D-11E6-9FB8-0002A5D5C51B"],
  134. rssi=-90,
  135. )
  136. result = parse_advertisement_data(ble_device, adv_data)
  137. assert result == SwitchBotAdvertisement(
  138. address="aa:bb:cc:dd:ee:ff",
  139. data={
  140. "rawAdvData": b"H\x10\xe1",
  141. "data": {"switchMode": False, "isOn": False, "battery": 97},
  142. "model": "H",
  143. "isEncrypted": False,
  144. "modelFriendlyName": "Bot",
  145. "modelName": SwitchbotModel.BOT,
  146. },
  147. device=ble_device,
  148. rssi=-90,
  149. )