| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 |
- from unittest.mock import AsyncMock
- import pytest
- from switchbot import (
- SwitchBotAdvertisement,
- SwitchbotModel,
- SwitchbotOutdoorPlug,
- parse_advertisement_data,
- )
- from switchbot.adv_parsers.plug import process_outdoor_plug
- from switchbot.devices.device import API_MODEL_TO_ENUM
- from .test_adv_parser import generate_advertisement_data, generate_ble_device
- @pytest.mark.parametrize("network", [0, 0x15, 0x75])
- @pytest.mark.parametrize(("status", "is_on"), [(0x01, False), (0x87, True)])
- @pytest.mark.parametrize("encrypted", [False, True])
- def test_outdoor_plug_eu_advertisement(network, status, is_on, encrypted):
- service = bytes([0x80 if encrypted else 0, 0, 0, 0, 0x12, 0x18, 0xD0, network])
- manufacturer = (
- bytes.fromhex("010203040506")
- + bytes([99, status, 0, 0])
- + bytes.fromhex("04e6007b0000")
- )
- adv = parse_advertisement_data(
- generate_ble_device("01:02:03:04:05:06", "Outdoor Plug (EU)"),
- generate_advertisement_data(
- manufacturer_data={2409: manufacturer},
- service_data={"0000fd3d-0000-1000-8000-00805f9b34fb": service},
- ),
- )
- assert adv is not None
- assert adv.data["modelName"] == SwitchbotModel.OUTDOOR_PLUG_EU
- assert adv.data["modelFriendlyName"] == "Outdoor Plug (EU)"
- assert adv.data["isEncrypted"] is encrypted
- assert adv.data["data"] == {
- "switchMode": True,
- "sequence_number": 99,
- "isOn": is_on,
- "power": 1254.0,
- "power_production": 123.0,
- }
- def create_device():
- return SwitchbotOutdoorPlug(
- generate_ble_device("01:02:03:04:05:06", "Outdoor Plug"),
- "ff",
- "ffffffffffffffffffffffffffffffff",
- )
- @pytest.mark.asyncio
- @pytest.mark.parametrize("is_on", [False, True])
- async def test_bidirectional_metering(is_on):
- device = create_device()
- device.get_current_time_and_start_time = lambda: ("00000010", "00000000")
- common = bytes([1, 99, 0x81 if is_on else 1]) + bytes(13) + bytes([3])
- consumed = bytes.fromhex("0100ea60003a98003c08fc03e808fc")
- produced = bytes.fromhex("0100bb80007530001e08fc07d011f8")
- device._get_basic_info = AsyncMock(side_effect=[common, consumed, produced])
- info = await device.get_basic_info()
- assert info["isOn"] is is_on
- assert info["energy"] == 1.0
- assert info["energy_production"] == 0.8
- assert info["power"] == (230.0 if is_on else 0)
- assert info["power_production"] == (460.0 if is_on else 0)
- assert info["current"] == (1.0 if is_on else 0)
- assert info["current_production"] == (2.0 if is_on else 0)
- assert info["voltage"] == (230 if is_on else 0)
- assert device._get_basic_info.call_args_list[1].args == (
- "570f7106000000001000000000",
- )
- assert device._get_basic_info.call_args_list[2].args == (
- "570f7106010000001000000000",
- )
- @pytest.mark.asyncio
- @pytest.mark.parametrize("reply", [None, b"", b"\x01", bytes(14)])
- async def test_short_production_response(reply):
- device = create_device()
- common = bytes([1, 99, 0x81]) + bytes(13) + bytes([3])
- device._get_basic_info = AsyncMock(side_effect=[common, bytes(15), reply])
- assert await device.get_basic_info() is None
- @pytest.mark.parametrize("manufacturer", [None, b"", bytes(13)])
- def test_short_advertisement(manufacturer):
- assert process_outdoor_plug(None, manufacturer) == {}
- @pytest.mark.asyncio
- async def test_advertisement_preserves_energy_and_refreshes_power():
- device = create_device()
- device.update = AsyncMock()
- def advertisement(sequence, power, production):
- return SwitchBotAdvertisement(
- address="01:02:03:04:05:06",
- data={
- "data": {
- "sequence_number": sequence,
- "isOn": True,
- "power": power,
- "power_production": production,
- }
- },
- device=device._device,
- rssi=-40,
- active=True,
- )
- device.update_from_advertisement(advertisement(1, 20, 30))
- device.data["data"].update(
- {"energy": 1.2, "energy_production": 3.4, "current_production": 0.5}
- )
- device.update_from_advertisement(advertisement(2, 40, 50))
- assert device.parsed_data["energy"] == 1.2
- assert device.parsed_data["energy_production"] == 3.4
- assert device.parsed_data["current_production"] == 0.5
- assert device.parsed_data["power"] == 40
- assert device.parsed_data["power_production"] == 50
- def test_cloud_model_mapping():
- assert API_MODEL_TO_ENUM["W1186000"] is SwitchbotModel.OUTDOOR_PLUG_EU
|