test_fan.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. from unittest.mock import AsyncMock, MagicMock
  2. import pytest
  3. from bleak.backends.device import BLEDevice
  4. from switchbot import SwitchBotAdvertisement, SwitchbotModel
  5. from switchbot.const.fan import FanMode
  6. from switchbot.devices import fan
  7. from .test_adv_parser import generate_ble_device
  8. def create_device_for_command_testing(
  9. init_data: dict | None = None, model: SwitchbotModel = SwitchbotModel.CIRCULATOR_FAN
  10. ):
  11. ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
  12. fan_device = fan.SwitchbotFan(ble_device, model=model)
  13. fan_device.update_from_advertisement(make_advertisement_data(ble_device, init_data))
  14. fan_device._send_command = AsyncMock()
  15. fan_device._check_command_result = MagicMock()
  16. fan_device.update = AsyncMock()
  17. return fan_device
  18. def make_advertisement_data(ble_device: BLEDevice, init_data: dict | None = None):
  19. """Set advertisement data with defaults."""
  20. if init_data is None:
  21. init_data = {}
  22. return SwitchBotAdvertisement(
  23. address="aa:bb:cc:dd:ee:ff",
  24. data={
  25. "rawAdvData": b"~\x00R",
  26. "data": {
  27. "isOn": True,
  28. "mode": "NORMAL",
  29. "nightLight": 3,
  30. "oscillating": False,
  31. "battery": 60,
  32. "speed": 50,
  33. }
  34. | init_data,
  35. "isEncrypted": False,
  36. "model": ",",
  37. "modelFriendlyName": "Circulator Fan",
  38. "modelName": SwitchbotModel.CIRCULATOR_FAN,
  39. },
  40. device=ble_device,
  41. rssi=-80,
  42. active=True,
  43. )
  44. @pytest.mark.asyncio
  45. @pytest.mark.parametrize(
  46. ("response", "expected"),
  47. [
  48. (b"\x00", None),
  49. (b"\x07", None),
  50. (b"\x01\x02\x03", b"\x01\x02\x03"),
  51. ],
  52. )
  53. async def test__get_basic_info(response, expected):
  54. fan_device = create_device_for_command_testing()
  55. fan_device._send_command = AsyncMock(return_value=response)
  56. result = await fan_device._get_basic_info(cmd="TEST_CMD")
  57. assert result == expected
  58. @pytest.mark.asyncio
  59. @pytest.mark.parametrize(
  60. ("basic_info", "firmware_info"), [(True, False), (False, True), (False, False)]
  61. )
  62. async def test_get_basic_info_returns_none(basic_info, firmware_info):
  63. fan_device = create_device_for_command_testing()
  64. async def mock_get_basic_info(arg):
  65. if arg == fan.COMMAND_GET_BASIC_INFO:
  66. return basic_info
  67. if arg == fan.DEVICE_GET_BASIC_SETTINGS_KEY:
  68. return firmware_info
  69. return None
  70. fan_device._get_basic_info = AsyncMock(side_effect=mock_get_basic_info)
  71. assert await fan_device.get_basic_info() is None
  72. @pytest.mark.asyncio
  73. @pytest.mark.parametrize(
  74. ("basic_info", "firmware_info", "result"),
  75. [
  76. (
  77. bytearray(b"\x01\x02W\x82g\xf5\xde4\x01=dPP\x03\x14P\x00\x00\x00\x00"),
  78. bytearray(b"\x01W\x0b\x17\x01"),
  79. [87, True, False, "normal", 61, 1.1],
  80. ),
  81. (
  82. bytearray(b"\x01\x02U\xc2g\xf5\xde4\x04+dPP\x03\x14P\x00\x00\x00\x00"),
  83. bytearray(b"\x01U\x0b\x17\x01"),
  84. [85, True, True, "baby", 43, 1.1],
  85. ),
  86. ],
  87. )
  88. async def test_get_basic_info(basic_info, firmware_info, result):
  89. fan_device = create_device_for_command_testing()
  90. async def mock_get_basic_info(arg):
  91. if arg == fan.COMMAND_GET_BASIC_INFO:
  92. return basic_info
  93. if arg == fan.DEVICE_GET_BASIC_SETTINGS_KEY:
  94. return firmware_info
  95. return None
  96. fan_device._get_basic_info = AsyncMock(side_effect=mock_get_basic_info)
  97. info = await fan_device.get_basic_info()
  98. assert info["battery"] == result[0]
  99. assert info["isOn"] == result[1]
  100. assert info["oscillating"] == result[2]
  101. assert info["mode"] == result[3]
  102. assert info["speed"] == result[4]
  103. assert info["firmware"] == result[5]
  104. @pytest.mark.asyncio
  105. async def test_set_preset_mode():
  106. fan_device = create_device_for_command_testing({"mode": "baby"})
  107. await fan_device.set_preset_mode("baby")
  108. assert fan_device.get_current_mode() == "baby"
  109. @pytest.mark.asyncio
  110. async def test_set_percentage_with_speed_is_0():
  111. fan_device = create_device_for_command_testing({"speed": 0, "isOn": False})
  112. await fan_device.turn_off()
  113. assert fan_device.get_current_percentage() == 0
  114. assert fan_device.is_on() is False
  115. @pytest.mark.asyncio
  116. async def test_set_percentage():
  117. fan_device = create_device_for_command_testing({"speed": 80})
  118. await fan_device.set_percentage(80)
  119. assert fan_device.get_current_percentage() == 80
  120. @pytest.mark.asyncio
  121. async def test_set_not_oscillation():
  122. fan_device = create_device_for_command_testing({"oscillating": False})
  123. await fan_device.set_oscillation(False)
  124. assert fan_device.get_oscillating_state() is False
  125. @pytest.mark.asyncio
  126. async def test_set_oscillation():
  127. fan_device = create_device_for_command_testing({"oscillating": True})
  128. await fan_device.set_oscillation(True)
  129. assert fan_device.get_oscillating_state() is True
  130. @pytest.mark.asyncio
  131. async def test_turn_on():
  132. fan_device = create_device_for_command_testing({"isOn": True})
  133. await fan_device.turn_on()
  134. assert fan_device.is_on() is True
  135. @pytest.mark.asyncio
  136. async def test_turn_off():
  137. fan_device = create_device_for_command_testing({"isOn": False})
  138. await fan_device.turn_off()
  139. assert fan_device.is_on() is False
  140. def test_get_modes():
  141. assert FanMode.get_modes() == ["normal", "natural", "sleep", "baby"]