| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249 |
- from unittest.mock import AsyncMock
- import pytest
- from bleak.backends.device import BLEDevice
- from switchbot import SwitchbotOperationError
- from switchbot.devices.meter_pro import MAX_TIME_OFFSET, SwitchbotMeterProCO2
- def create_device():
- ble_device = BLEDevice(
- address="aa:bb:cc:dd:ee:ff", name="any", details={"rssi": -80}
- )
- device = SwitchbotMeterProCO2(ble_device)
- device._send_command = AsyncMock()
- return device
- @pytest.mark.asyncio
- @pytest.mark.parametrize(
- (
- "device_response",
- "expected_offset",
- ),
- [
- ("0100101bc9", 1055689), # 01 (success) 00 (plus offset) 10 1b c9 (1055689)
- ("0180101bc9", -1055689), # 01 (success) 80 (minus offset) 10 1b c9 (1055689)
- ],
- )
- async def test_get_time_offset(device_response: str, expected_offset: int):
- device = create_device()
- device._send_command.return_value = bytes.fromhex(device_response)
- offset = await device.get_time_offset()
- device._send_command.assert_called_with("570f690506")
- assert offset == expected_offset
- @pytest.mark.asyncio
- async def test_get_time_offset_failure():
- device = create_device()
- # Invalid 1st byte
- device._send_command.return_value = bytes.fromhex("0080101bc9")
- with pytest.raises(SwitchbotOperationError):
- await device.get_time_offset()
- device._send_command.assert_called_with("570f690506")
- @pytest.mark.asyncio
- async def test_get_time_offset_wrong_response():
- device = create_device()
- # Response too short (only status byte returned)
- device._send_command.return_value = bytes.fromhex("01")
- with pytest.raises(SwitchbotOperationError):
- await device.get_time_offset()
- @pytest.mark.asyncio
- @pytest.mark.parametrize(
- (
- "offset_sec",
- "expected_payload",
- ),
- [
- (1055689, "00101bc9"), # "00" for positive offset, 101bc9 for 1055689
- (-4096, "80001000"), # "80" for negative offset, 001000 for 4096
- (0, "00000000"),
- (-0, "00000000"), # -0 == 0 in Python
- ],
- )
- async def test_set_time_offset(offset_sec: int, expected_payload: str):
- device = create_device()
- device._send_command.return_value = bytes.fromhex("01")
- await device.set_time_offset(offset_sec)
- device._send_command.assert_called_with("570f680506" + expected_payload)
- @pytest.mark.asyncio
- async def test_set_time_offset_too_large():
- device = create_device()
- with pytest.raises(SwitchbotOperationError):
- await device.set_time_offset(MAX_TIME_OFFSET + 1)
- with pytest.raises(SwitchbotOperationError):
- await device.set_time_offset(-(MAX_TIME_OFFSET + 1))
- @pytest.mark.asyncio
- async def test_set_time_offset_failure():
- device = create_device()
- device._send_command.return_value = bytes.fromhex("00")
- with pytest.raises(SwitchbotOperationError):
- await device.set_time_offset(100)
- @pytest.mark.asyncio
- async def test_get_datetime_success():
- device = create_device()
- # Mock response:
- # byte 0: 01 (success)
- # bytes 1-4: e4 02 94 23 (temp, ignored)
- # byte 5: 00 (24h mode)
- # bytes 6-7: 07 e9 (year 2025)
- # byte 8: 0c (Dec)
- # byte 9: 1e (30)
- # byte 10: 08 (Hour)
- # byte 11: 37 (Minute = 55)
- # byte 12: 01 (Second)
- response_hex = "01e40294230007e90c1e083701"
- device._send_command.return_value = bytes.fromhex(response_hex)
- result = await device.get_datetime()
- device._send_command.assert_called_with("570f6901")
- assert result["12h_mode"] is False
- assert result["year"] == 2025
- assert result["month"] == 12
- assert result["day"] == 30
- assert result["hour"] == 8
- assert result["minute"] == 55
- assert result["second"] == 1
- @pytest.mark.asyncio
- async def test_get_datetime_12h_mode():
- device = create_device()
- # byte 5: 80 (12h mode)
- # Time: 12:00:00
- response_hex = "010000000080000001010c0000"
- device._send_command.return_value = bytes.fromhex(response_hex)
- result = await device.get_datetime()
- device._send_command.assert_called_with("570f6901")
- assert result["12h_mode"] is True
- assert result["year"] == 0
- assert result["month"] == 1
- assert result["day"] == 1
- assert result["hour"] == 12
- assert result["minute"] == 0
- assert result["second"] == 0
- @pytest.mark.asyncio
- async def test_get_datetime_failure():
- device = create_device()
- device._send_command.return_value = bytes.fromhex("00")
- with pytest.raises(SwitchbotOperationError):
- await device.get_datetime()
- @pytest.mark.asyncio
- async def test_get_datetime_wrong_response():
- device = create_device()
- device._send_command.return_value = bytes.fromhex("0100")
- with pytest.raises(SwitchbotOperationError):
- await device.get_datetime()
- @pytest.mark.asyncio
- @pytest.mark.parametrize(
- (
- "timestamp",
- "utc_offset_hours",
- "utc_offset_minutes",
- "expected_ts",
- "expected_utc",
- "expected_min",
- ),
- [
- (1709251200, 0, 0, "65e11a80", "0c", "00"), # 2024-03-01T00:00:00+00:00
- (1709251200, 1, 0, "65e11a80", "0d", "00"), # 2024-03-01T00:00:00+01:00
- (1709251200, 5, 45, "65e1250c", "11", "2d"), # 2024-03-01T00:00:00+05:45
- (1709251200, -6, 15, "65e11e04", "06", "0f"), # 2024-03-01T00:00:00-05:45
- ],
- )
- async def test_set_datetime( # noqa: PLR0913
- timestamp: int,
- utc_offset_hours: int,
- utc_offset_minutes: int,
- expected_ts: str,
- expected_utc: str,
- expected_min: str,
- ):
- device = create_device()
- device._send_command.return_value = bytes.fromhex("01")
- await device.set_datetime(
- timestamp,
- utc_offset_hours=utc_offset_hours,
- utc_offset_minutes=utc_offset_minutes,
- )
- expected_ts = expected_ts.zfill(16)
- expected_payload = "57000503" + expected_utc + expected_ts + expected_min
- device._send_command.assert_called_with(expected_payload)
- @pytest.mark.asyncio
- @pytest.mark.parametrize(
- "bad_hour",
- [-13, 15],
- )
- async def test_set_datetime_invalid_utc_offset_hours(bad_hour: int):
- device = create_device()
- with pytest.raises(SwitchbotOperationError):
- await device.set_datetime(1709251200, utc_offset_hours=bad_hour)
- @pytest.mark.asyncio
- @pytest.mark.parametrize(
- "bad_min",
- [-1, 60],
- )
- async def test_set_datetime_invalid_utc_offset_minutes(bad_min: int):
- device = create_device()
- with pytest.raises(SwitchbotOperationError):
- await device.set_datetime(1709251200, utc_offset_minutes=bad_min)
- @pytest.mark.asyncio
- @pytest.mark.parametrize(
- ("is_12h_mode", "expected_payload"),
- [
- (True, "80"),
- (False, "00"),
- ],
- )
- async def test_set_time_display_format(is_12h_mode: bool, expected_payload: str):
- device = create_device()
- device._send_command.return_value = bytes.fromhex("01")
- await device.set_time_display_format(is_12h_mode=is_12h_mode)
- device._send_command.assert_called_with("570f680505" + expected_payload)
- @pytest.mark.asyncio
- async def test_set_time_display_format_failure():
- device = create_device()
- device._send_command.return_value = bytes.fromhex("00")
- with pytest.raises(SwitchbotOperationError):
- await device.set_time_display_format(is_12h_mode=True)
|