123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357 |
- import datetime
- from unittest.mock import AsyncMock, MagicMock, patch
- import pytest
- from bleak.backends.device import BLEDevice
- from switchbot import (
- HumidifierAction,
- HumidifierMode,
- HumidifierWaterLevel,
- SwitchBotAdvertisement,
- SwitchbotModel,
- )
- from switchbot.devices import evaporative_humidifier
- from switchbot.devices.device import SwitchbotEncryptedDevice, SwitchbotOperationError
- from .test_adv_parser import generate_ble_device
- def create_device_for_command_testing(init_data: dict | None = None):
- ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
- evaporative_humidifier_device = (
- evaporative_humidifier.SwitchbotEvaporativeHumidifier(
- ble_device, "ff", "ffffffffffffffffffffffffffffffff"
- )
- )
- evaporative_humidifier_device.update_from_advertisement(
- make_advertisement_data(ble_device, init_data)
- )
- evaporative_humidifier_device._send_command = AsyncMock()
- evaporative_humidifier_device._check_command_result = MagicMock()
- evaporative_humidifier_device.update = AsyncMock()
- return evaporative_humidifier_device
- def make_advertisement_data(ble_device: BLEDevice, init_data: dict | None = None):
- if init_data is None:
- init_data = {}
- """Set advertisement data with defaults."""
- return SwitchBotAdvertisement(
- address="aa:bb:cc:dd:ee:ff",
- data={
- "rawAdvData": b"#\x00\x00\x15\x1c\x00",
- "data": {
- "isOn": False,
- "mode": None,
- "target_humidity": 52,
- "child_lock": False,
- "over_humidify_protection": True,
- "tank_removed": False,
- "tilted_alert": False,
- "filter_missing": False,
- "is_meter_binded": True,
- "humidity": 51,
- "temperature": 16.8,
- "temp": {"c": 16.8, "f": 62.24},
- "filter_run_time": datetime.timedelta(days=3, seconds=57600),
- "filter_alert": False,
- "water_level": "medium",
- }
- | init_data,
- "isEncrypted": False,
- "model": "#",
- "modelFriendlyName": "Evaporative Humidifier",
- "modelName": SwitchbotModel.EVAPORATIVE_HUMIDIFIER,
- },
- device=ble_device,
- rssi=-80,
- active=True,
- )
- @pytest.mark.asyncio
- async def test_turn_on():
- """Test the turn_on method."""
- device = create_device_for_command_testing({"isOn": True})
- await device.turn_on()
- assert device.is_on() is True
- @pytest.mark.asyncio
- async def test_turn_off():
- """Test the turn_off method."""
- device = create_device_for_command_testing({"isOn": False})
- await device.turn_off()
- assert device.is_on() is False
- @pytest.mark.asyncio
- async def test_get_basic_is_none():
- """Test the get_basic_info when it returns None."""
- device = create_device_for_command_testing()
- device._get_basic_info = AsyncMock(return_value=None)
- assert await device.get_basic_info() is None
- @pytest.mark.asyncio
- @pytest.mark.parametrize(
- ("basic_info", "result"),
- [
- (
- bytearray(b"\x01\x86\x88\xb1\x98\x82\x00\x1e\x00\x88-\xc4\xff\xff \n\x07"),
- [
- True,
- HumidifierMode(6),
- True,
- False,
- False,
- False,
- False,
- True,
- 49,
- 24.8,
- 24.8,
- 76.64,
- "medium",
- 30,
- 45,
- ],
- ),
- (
- bytearray(b"\x01\x08 \xb1\x98r\x00\x1e\x00\x89-\xc4\xff\xff\x00\x00\x00"),
- [
- False,
- HumidifierMode(8),
- False,
- True,
- False,
- False,
- False,
- True,
- 49,
- 24.7,
- 24.7,
- 76.46,
- "medium",
- 30,
- 45,
- ],
- ),
- ],
- )
- async def test_get_basic_info(basic_info, result):
- """Test the get_basic_info method."""
- device = create_device_for_command_testing()
- device._get_basic_info = AsyncMock(return_value=basic_info)
- info = await device.get_basic_info()
- assert info["isOn"] is result[0]
- assert info["mode"] == result[1]
- assert info["over_humidify_protection"] is result[2]
- assert info["child_lock"] is result[3]
- assert info["tank_removed"] is result[4]
- assert info["tilted_alert"] is result[5]
- assert info["filter_missing"] is result[6]
- assert info["is_meter_binded"] is result[7]
- assert info["humidity"] == result[8]
- assert info["temperature"] == result[9]
- assert info["temp"]["c"] == result[10]
- assert info["temp"]["f"] == result[11]
- assert info["water_level"] == result[12]
- assert info["filter_run_time"] == result[13]
- assert info["target_humidity"] == result[14]
- @pytest.mark.asyncio
- @pytest.mark.parametrize(
- ("err_msg", "mode", "water_level"),
- [
- (
- "Target humidity can only be set in target humidity mode or sleep mode",
- HumidifierMode.AUTO,
- "low",
- ),
- (
- "Cannot perform operation when water tank is empty",
- HumidifierMode.TARGET_HUMIDITY,
- "empty",
- ),
- ],
- )
- async def test_set_target_humidity_with_invalid_conditions(err_msg, mode, water_level):
- """Test setting target humidity with invalid mode."""
- device = create_device_for_command_testing()
- device.get_mode = MagicMock(return_value=mode)
- device.get_water_level = MagicMock(return_value=water_level)
- with pytest.raises(SwitchbotOperationError, match=err_msg):
- await device.set_target_humidity(45)
- @pytest.mark.asyncio
- @pytest.mark.parametrize(
- ("err_msg", "mode", "water_level", "is_meter_binded", "target_humidity"),
- [
- (
- "Cannot perform operation when water tank is empty",
- HumidifierMode.TARGET_HUMIDITY,
- "empty",
- True,
- 45,
- ),
- (
- "Cannot set target humidity or auto mode when meter is not binded",
- HumidifierMode.TARGET_HUMIDITY,
- "medium",
- False,
- 45,
- ),
- (
- "Target humidity must be set before switching to target humidity mode or sleep mode",
- HumidifierMode.TARGET_HUMIDITY,
- "medium",
- True,
- None,
- ),
- ],
- )
- async def test_set_mode_with_invalid_conditions(
- err_msg, mode, water_level, is_meter_binded, target_humidity
- ):
- """Test setting target humidity with invalid mode."""
- device = create_device_for_command_testing()
- device.get_water_level = MagicMock(return_value=water_level)
- device.is_meter_binded = MagicMock(return_value=is_meter_binded)
- device.get_target_humidity = MagicMock(return_value=target_humidity)
- with pytest.raises(SwitchbotOperationError, match=err_msg):
- await device.set_mode(mode)
- @pytest.mark.asyncio
- async def test_set_target_humidity():
- """Test setting target humidity."""
- device = create_device_for_command_testing()
- device.get_mode = MagicMock(return_value=HumidifierMode.TARGET_HUMIDITY)
- await device.set_target_humidity(45)
- device._send_command.assert_awaited_once_with("570f430202002d")
- @pytest.mark.asyncio
- @pytest.mark.parametrize(
- ("mode", "command"),
- [
- (HumidifierMode.TARGET_HUMIDITY, "570f430202002d"),
- (HumidifierMode.AUTO, "570f4302040000"),
- (HumidifierMode.SLEEP, "570f430203002d"),
- (HumidifierMode.DRYING_FILTER, "570f43010108"),
- ],
- )
- async def test_set_mode(mode, command):
- """Test setting mode."""
- device = create_device_for_command_testing()
- device.get_target_humidity = MagicMock(return_value=45)
- await device.set_mode(mode)
- device._send_command.assert_awaited_once_with(command)
- @pytest.mark.asyncio
- @pytest.mark.parametrize(
- ("init_data", "result"),
- [
- (
- {"isOn": False, "mode": HumidifierMode.AUTO},
- [False, HumidifierMode.AUTO, HumidifierAction.OFF],
- ),
- (
- {"isOn": True, "mode": HumidifierMode.TARGET_HUMIDITY},
- [True, HumidifierMode.TARGET_HUMIDITY, HumidifierAction.HUMIDIFYING],
- ),
- (
- {"isOn": True, "mode": HumidifierMode.DRYING_FILTER},
- [True, HumidifierMode.DRYING_FILTER, HumidifierAction.DRYING],
- ),
- ],
- )
- async def test_status_from_process_adv(init_data, result):
- """Test status from process advertisement."""
- device = create_device_for_command_testing(init_data)
- assert device.is_on() is result[0]
- assert device.get_mode() is result[1]
- assert device.is_child_lock_enabled() is False
- assert device.is_over_humidify_protection_enabled() is True
- assert device.is_tank_removed() is False
- assert device.is_filter_missing() is False
- assert device.is_filter_alert_on() is False
- assert device.is_tilted_alert_on() is False
- assert device.get_water_level() == "medium"
- assert device.get_filter_run_time() == datetime.timedelta(days=3, seconds=57600)
- assert device.get_target_humidity() == 52
- assert device.get_humidity() == 51
- assert device.get_temperature() == 16.8
- assert device.get_action() == result[2]
- assert device.is_meter_binded() is True
- @pytest.mark.asyncio
- @pytest.mark.parametrize(
- ("enabled", "command"),
- [
- (True, "570f430501"),
- (False, "570f430500"),
- ],
- )
- async def test_set_child_lock(enabled, command):
- """Test setting child lock."""
- device = create_device_for_command_testing()
- await device.set_child_lock(enabled)
- device._send_command.assert_awaited_once_with(command)
- @pytest.mark.asyncio
- @patch.object(SwitchbotEncryptedDevice, "verify_encryption_key", new_callable=AsyncMock)
- async def test_verify_encryption_key(mock_parent_verify):
- ble_device = generate_ble_device("aa:bb:cc:dd:ee:ff", "any")
- key_id = "ff"
- encryption_key = "ffffffffffffffffffffffffffffffff"
- mock_parent_verify.return_value = True
- result = await evaporative_humidifier.SwitchbotEvaporativeHumidifier.verify_encryption_key(
- device=ble_device,
- key_id=key_id,
- encryption_key=encryption_key,
- )
- mock_parent_verify.assert_awaited_once_with(
- ble_device,
- key_id,
- encryption_key,
- SwitchbotModel.EVAPORATIVE_HUMIDIFIER,
- )
- assert result is True
- def test_evaporative_humidifier_modes():
- assert HumidifierMode.get_modes() == [
- "high",
- "medium",
- "low",
- "quiet",
- "target_humidity",
- "sleep",
- "auto",
- "drying_filter",
- ]
- def test_evaporative_humidifier_water_levels():
- assert HumidifierWaterLevel.get_levels() == [
- "empty",
- "low",
- "medium",
- "high",
- ]
|