1
0

test_bulb.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  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.light import ColorMode
  6. from switchbot.devices import bulb
  7. from switchbot.devices.device import SwitchbotOperationError
  8. from .test_adv_parser import generate_ble_device
  9. def create_device_for_command_testing(init_data: dict | None = None):
  10. ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
  11. device = bulb.SwitchbotBulb(ble_device)
  12. device.update_from_advertisement(make_advertisement_data(ble_device, init_data))
  13. device._send_command = AsyncMock()
  14. device._check_command_result = MagicMock()
  15. device.update = AsyncMock()
  16. return device
  17. def make_advertisement_data(ble_device: BLEDevice, init_data: dict | None = None):
  18. """Set advertisement data with defaults."""
  19. if init_data is None:
  20. init_data = {}
  21. return SwitchBotAdvertisement(
  22. address="aa:bb:cc:dd:ee:ff",
  23. data={
  24. "rawAdvData": b"u\x00d",
  25. "data": {
  26. "brightness": 1,
  27. "color_mode": 2,
  28. "delay": False,
  29. "isOn": True,
  30. "loop_index": 0,
  31. "preset": False,
  32. "sequence_number": 2,
  33. "speed": 0,
  34. }
  35. | init_data,
  36. "isEncrypted": False,
  37. "model": "u",
  38. "modelFriendlyName": "Color Bulb",
  39. "modelName": SwitchbotModel.COLOR_BULB,
  40. },
  41. device=ble_device,
  42. rssi=-80,
  43. active=True,
  44. )
  45. @pytest.mark.asyncio
  46. async def test_default_info():
  47. """Test default initialization of the color bulb."""
  48. device = create_device_for_command_testing()
  49. assert device.rgb is None
  50. device._state = {"r": 30, "g": 0, "b": 0, "cw": 3200}
  51. assert device.is_on() is True
  52. assert device.on is True
  53. assert device.color_mode == ColorMode.RGB
  54. assert device.color_modes == {ColorMode.RGB, ColorMode.COLOR_TEMP}
  55. assert device.rgb == (30, 0, 0)
  56. assert device.color_temp == 3200
  57. assert device.brightness == 1
  58. assert device.min_temp == 2700
  59. assert device.max_temp == 6500
  60. assert device.get_effect_list == list(bulb.EFFECT_DICT.keys())
  61. @pytest.mark.asyncio
  62. @pytest.mark.parametrize(
  63. ("basic_info", "version_info"), [(True, False), (False, True), (False, False)]
  64. )
  65. async def test_get_basic_info_returns_none(basic_info, version_info):
  66. device = create_device_for_command_testing()
  67. async def mock_get_basic_info(arg):
  68. if arg == bulb.DEVICE_GET_BASIC_SETTINGS_KEY:
  69. return basic_info
  70. if arg == bulb.DEVICE_GET_VERSION_KEY:
  71. return version_info
  72. return None
  73. device._get_basic_info = AsyncMock(side_effect=mock_get_basic_info)
  74. assert await device.get_basic_info() is None
  75. @pytest.mark.asyncio
  76. @pytest.mark.parametrize(
  77. ("info_data", "result"),
  78. [
  79. (
  80. {
  81. "basic_info": b"\x01\x80\x01\xff\x91\x96\x00\x00\xff\xff\x02",
  82. "version_info": b"\x01\x01\x11",
  83. },
  84. [True, 1, 255, 145, 150, 0, 2, 1.7],
  85. ),
  86. (
  87. {
  88. "basic_info": b"\x01\x80;\x00\x00\x00\x0c\x99\xff\xff\x01",
  89. "version_info": b"\x01\x01\x11",
  90. },
  91. [True, 59, 0, 0, 0, 3225, 1, 1.7],
  92. ),
  93. (
  94. {
  95. "basic_info": b"\x01\x80\t!7\xff\x00\x00\xff\xff\x02",
  96. "version_info": b"\x01\x01\x11",
  97. },
  98. [True, 9, 33, 55, 255, 0, 2, 1.7],
  99. ),
  100. ],
  101. )
  102. async def test_get_basic_info(info_data, result):
  103. """Test getting basic info from the color bulb."""
  104. device = create_device_for_command_testing()
  105. async def mock_get_basic_info(args: str) -> list[int] | None:
  106. if args == bulb.DEVICE_GET_BASIC_SETTINGS_KEY:
  107. return info_data["basic_info"]
  108. if args == bulb.DEVICE_GET_VERSION_KEY:
  109. return info_data["version_info"]
  110. return None
  111. device._get_basic_info = AsyncMock(side_effect=mock_get_basic_info)
  112. info = await device.get_basic_info()
  113. assert info["isOn"] is result[0]
  114. assert info["brightness"] == result[1]
  115. assert info["r"] == result[2]
  116. assert info["g"] == result[3]
  117. assert info["b"] == result[4]
  118. assert info["cw"] == result[5]
  119. assert info["color_mode"] == result[6]
  120. assert info["firmware"] == result[7]
  121. @pytest.mark.asyncio
  122. async def test_set_color_temp():
  123. """Test setting color temperature."""
  124. device = create_device_for_command_testing()
  125. await device.set_color_temp(50, 3000)
  126. device._send_command.assert_called_with(f"{bulb.CW_BRIGHTNESS_KEY}320BB8")
  127. @pytest.mark.asyncio
  128. async def test_turn_on():
  129. """Test turning on the color bulb."""
  130. device = create_device_for_command_testing({"isOn": True})
  131. await device.turn_on()
  132. device._send_command.assert_called_with(bulb.BULB_ON_KEY)
  133. assert device.is_on() is True
  134. @pytest.mark.asyncio
  135. async def test_turn_off():
  136. """Test turning off the color bulb."""
  137. device = create_device_for_command_testing({"isOn": False})
  138. await device.turn_off()
  139. device._send_command.assert_called_with(bulb.BULB_OFF_KEY)
  140. assert device.is_on() is False
  141. @pytest.mark.asyncio
  142. async def test_set_brightness():
  143. """Test setting brightness."""
  144. device = create_device_for_command_testing()
  145. await device.set_brightness(75)
  146. device._send_command.assert_called_with(f"{bulb.BRIGHTNESS_KEY}4B")
  147. @pytest.mark.asyncio
  148. async def test_set_rgb():
  149. """Test setting RGB values."""
  150. device = create_device_for_command_testing()
  151. await device.set_rgb(100, 255, 128, 64)
  152. device._send_command.assert_called_with(f"{bulb.RGB_BRIGHTNESS_KEY}64FF8040")
  153. @pytest.mark.asyncio
  154. async def test_set_effect_with_invalid_effect():
  155. """Test setting an invalid effect."""
  156. device = create_device_for_command_testing()
  157. with pytest.raises(
  158. SwitchbotOperationError, match="Effect invalid_effect not supported"
  159. ):
  160. await device.set_effect("invalid_effect")
  161. @pytest.mark.asyncio
  162. async def test_set_effect_with_valid_effect():
  163. """Test setting a valid effect."""
  164. device = create_device_for_command_testing()
  165. await device.set_effect("Colorful")
  166. device._send_command.assert_called_with(bulb.EFFECT_DICT["Colorful"])
  167. assert device.get_effect() == "Colorful"