test_curtain.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. from typing import Any
  2. from unittest.mock import Mock
  3. import pytest
  4. from bleak.backends.device import BLEDevice
  5. from switchbot import SwitchBotAdvertisement, SwitchbotModel
  6. from switchbot.devices import curtain
  7. from .test_adv_parser import generate_ble_device
  8. def set_advertisement_data(ble_device: BLEDevice, in_motion: bool, position: int):
  9. """Set advertisement data with defaults."""
  10. return SwitchBotAdvertisement(
  11. address="aa:bb:cc:dd:ee:ff",
  12. data={
  13. "rawAdvData": b"c\xc0X\x00\x11\x04",
  14. "data": {
  15. "calibration": True,
  16. "battery": 88,
  17. "inMotion": in_motion,
  18. "position": position,
  19. "lightLevel": 1,
  20. "deviceChain": 1,
  21. },
  22. "isEncrypted": False,
  23. "model": "c",
  24. "modelFriendlyName": "Curtain",
  25. "modelName": SwitchbotModel.CURTAIN,
  26. },
  27. device=ble_device,
  28. rssi=-80,
  29. active=True,
  30. )
  31. @pytest.mark.parametrize("reverse_mode", [(True), (False)])
  32. def test_device_passive_not_in_motion(reverse_mode):
  33. """Test passive not in motion advertisement."""
  34. ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
  35. curtain_device = curtain.SwitchbotCurtain(ble_device, reverse_mode=reverse_mode)
  36. curtain_device.update_from_advertisement(
  37. set_advertisement_data(ble_device, False, 0)
  38. )
  39. assert curtain_device.is_opening() is False
  40. assert curtain_device.is_closing() is False
  41. @pytest.mark.parametrize("reverse_mode", [(True), (False)])
  42. def test_device_passive_opening(reverse_mode):
  43. """Test passive opening advertisement."""
  44. ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
  45. curtain_device = curtain.SwitchbotCurtain(ble_device, reverse_mode=reverse_mode)
  46. curtain_device.update_from_advertisement(
  47. set_advertisement_data(ble_device, True, 0)
  48. )
  49. curtain_device.update_from_advertisement(
  50. set_advertisement_data(ble_device, True, 10)
  51. )
  52. assert curtain_device.is_opening() is True
  53. assert curtain_device.is_closing() is False
  54. @pytest.mark.parametrize("reverse_mode", [(True), (False)])
  55. def test_device_passive_closing(reverse_mode):
  56. """Test passive closing advertisement."""
  57. ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
  58. curtain_device = curtain.SwitchbotCurtain(ble_device, reverse_mode=reverse_mode)
  59. curtain_device.update_from_advertisement(
  60. set_advertisement_data(ble_device, True, 100)
  61. )
  62. curtain_device.update_from_advertisement(
  63. set_advertisement_data(ble_device, True, 90)
  64. )
  65. assert curtain_device.is_opening() is False
  66. assert curtain_device.is_closing() is True
  67. @pytest.mark.parametrize("reverse_mode", [(True), (False)])
  68. def test_device_passive_opening_then_stop(reverse_mode):
  69. """Test passive stopped after opening advertisement."""
  70. ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
  71. curtain_device = curtain.SwitchbotCurtain(ble_device, reverse_mode=reverse_mode)
  72. curtain_device.update_from_advertisement(
  73. set_advertisement_data(ble_device, True, 0)
  74. )
  75. curtain_device.update_from_advertisement(
  76. set_advertisement_data(ble_device, True, 10)
  77. )
  78. curtain_device.update_from_advertisement(
  79. set_advertisement_data(ble_device, False, 10)
  80. )
  81. assert curtain_device.is_opening() is False
  82. assert curtain_device.is_closing() is False
  83. @pytest.mark.parametrize("reverse_mode", [(True), (False)])
  84. def test_device_passive_closing_then_stop(reverse_mode):
  85. """Test passive stopped after closing advertisement."""
  86. ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
  87. curtain_device = curtain.SwitchbotCurtain(ble_device, reverse_mode=reverse_mode)
  88. curtain_device.update_from_advertisement(
  89. set_advertisement_data(ble_device, True, 100)
  90. )
  91. curtain_device.update_from_advertisement(
  92. set_advertisement_data(ble_device, True, 90)
  93. )
  94. curtain_device.update_from_advertisement(
  95. set_advertisement_data(ble_device, False, 90)
  96. )
  97. assert curtain_device.is_opening() is False
  98. assert curtain_device.is_closing() is False
  99. @pytest.mark.asyncio
  100. @pytest.mark.parametrize("reverse_mode", [(True), (False)])
  101. async def test_device_active_not_in_motion(reverse_mode):
  102. """Test active not in motion."""
  103. ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
  104. curtain_device = curtain.SwitchbotCurtain(ble_device, reverse_mode=reverse_mode)
  105. curtain_device.update_from_advertisement(
  106. set_advertisement_data(ble_device, False, 0)
  107. )
  108. basic_info = bytes([0, 0, 0, 0, 0, 0, 100, 0])
  109. async def custom_implementation():
  110. return basic_info
  111. curtain_device._get_basic_info = Mock(side_effect=custom_implementation)
  112. await curtain_device.get_basic_info()
  113. assert curtain_device.is_opening() is False
  114. assert curtain_device.is_closing() is False
  115. @pytest.mark.asyncio
  116. @pytest.mark.parametrize("reverse_mode", [(True), (False)])
  117. async def test_device_active_opening(reverse_mode):
  118. """Test active opening."""
  119. ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
  120. curtain_device = curtain.SwitchbotCurtain(ble_device, reverse_mode=reverse_mode)
  121. curtain_device.update_from_advertisement(
  122. set_advertisement_data(ble_device, True, 0)
  123. )
  124. basic_info = bytes([0, 0, 0, 0, 0, 67, 10, 0])
  125. async def custom_implementation():
  126. return basic_info
  127. curtain_device._get_basic_info = Mock(side_effect=custom_implementation)
  128. await curtain_device.get_basic_info()
  129. assert curtain_device.is_opening() is True
  130. assert curtain_device.is_closing() is False
  131. @pytest.mark.asyncio
  132. @pytest.mark.parametrize("reverse_mode", [(True), (False)])
  133. async def test_device_active_closing(reverse_mode):
  134. """Test active closing."""
  135. ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
  136. curtain_device = curtain.SwitchbotCurtain(ble_device, reverse_mode=reverse_mode)
  137. curtain_device.update_from_advertisement(
  138. set_advertisement_data(ble_device, True, 100)
  139. )
  140. basic_info = bytes([0, 0, 0, 0, 0, 67, 90, 0])
  141. async def custom_implementation():
  142. return basic_info
  143. curtain_device._get_basic_info = Mock(side_effect=custom_implementation)
  144. await curtain_device.get_basic_info()
  145. assert curtain_device.is_opening() is False
  146. assert curtain_device.is_closing() is True
  147. @pytest.mark.asyncio
  148. @pytest.mark.parametrize("reverse_mode", [(True), (False)])
  149. async def test_device_active_opening_then_stop(reverse_mode):
  150. """Test active stopped after opening."""
  151. ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
  152. curtain_device = curtain.SwitchbotCurtain(ble_device, reverse_mode=reverse_mode)
  153. curtain_device.update_from_advertisement(
  154. set_advertisement_data(ble_device, True, 0)
  155. )
  156. basic_info = bytes([0, 0, 0, 0, 0, 67, 10, 0])
  157. async def custom_implementation():
  158. return basic_info
  159. curtain_device._get_basic_info = Mock(side_effect=custom_implementation)
  160. await curtain_device.get_basic_info()
  161. basic_info = bytes([0, 0, 0, 0, 0, 0, 10, 0])
  162. await curtain_device.get_basic_info()
  163. assert curtain_device.is_opening() is False
  164. assert curtain_device.is_closing() is False
  165. @pytest.mark.asyncio
  166. @pytest.mark.parametrize("reverse_mode", [(True), (False)])
  167. async def test_device_active_closing_then_stop(reverse_mode):
  168. """Test active stopped after closing."""
  169. ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
  170. curtain_device = curtain.SwitchbotCurtain(ble_device, reverse_mode=reverse_mode)
  171. curtain_device.update_from_advertisement(
  172. set_advertisement_data(ble_device, True, 100)
  173. )
  174. basic_info = bytes([0, 0, 0, 0, 0, 67, 90, 0])
  175. async def custom_implementation():
  176. return basic_info
  177. curtain_device._get_basic_info = Mock(side_effect=custom_implementation)
  178. await curtain_device.get_basic_info()
  179. basic_info = bytes([0, 0, 0, 0, 0, 0, 90, 0])
  180. await curtain_device.get_basic_info()
  181. assert curtain_device.is_opening() is False
  182. assert curtain_device.is_closing() is False