Esempio n. 1
0
    async def get_bulbtype(self) -> BulbType:
        """Return the bulb type as BulbType object."""
        if self.bulbtype is not None:
            return self.bulbtype

        bulb_config = await self.getBulbConfig()
        result = bulb_config["result"]
        white_range = await self.getExtendedWhiteRange()
        white_to_color_ratio = None
        white_channels = None
        if "drvConf" in result:
            # For old FW < 1.22
            white_to_color_ratio, white_channels = result["drvConf"]
        module_name = result.get(
            "moduleName")  # Not present in 1.8.0 firmware!
        fw_version = result.get("fwVersion")
        type_id = result.get("typeId")
        model_result = self.modelConfig["result"] if self.modelConfig else {}
        white_channels = model_result.get("nowc", white_channels)
        white_to_color_ratio = model_result.get("wcr", white_to_color_ratio)
        self.bulbtype = BulbType.from_data(
            module_name,
            white_range,
            fw_version,
            white_channels,
            white_to_color_ratio,
            type_id,
        )
        return self.bulbtype
Esempio n. 2
0
    async def get_bulbtype(self) -> BulbType:
        """Retrun the bulb type as BulbType object."""
        if self.bulbtype is None:
            bulb_config = await self.getBulbConfig()
            if "moduleName" in bulb_config["result"]:
                _bulbtype = bulb_config["result"]["moduleName"]
                # set the minimum features for dimmable bulbs (DW bulbs)
                # define the kelvin range
                _kelvin = await self.getExtendedWhiteRange()
                _bulb = BulbType(
                    bulb_type=BulbClass.DW,
                    name=_bulbtype,
                    features=Features(brightness=True,
                                      color=False,
                                      effect=False,
                                      color_tmp=False),
                    kelvin_range=None,
                )
                try:
                    # parse the features from name
                    _identifier = _bulbtype.split("_")[1]
                # Throw exception if index can not be found
                except IndexError:
                    raise WizLightNotKnownBulb(
                        "The bulb type can not be determined!")
                # go an try to map extensions to the BulbTyp object
                # Color support
                # TODO: Wokaround - In bulb firmware version 1.22.0 the k-range was removed.
                if "RGB" in _identifier:
                    if _kelvin:
                        _bulb.kelvin_range = KelvinRange(min=_kelvin[0],
                                                         max=_kelvin[1])
                    else:
                        _bulb.kelvin_range = KelvinRange(min=2700, max=6500)
                    _bulb.bulb_type = BulbClass.RGB
                    _bulb.features.color = True
                    # RGB supports effects and tuneabel white
                    _bulb.features.effect = True
                    _bulb.features.color_tmp = True
                # Non RGB but tunable white bulb
                if "TW" in _identifier:
                    if _kelvin:
                        _bulb.kelvin_range = KelvinRange(min=_kelvin[0],
                                                         max=_kelvin[1])
                    else:
                        _bulb.kelvin_range = KelvinRange(min=2700, max=6500)
                    _bulb.bulb_type = BulbClass.TW
                    _bulb.features.color_tmp = True
                    # RGB supports effects but only "some"
                    # TODO: Improve the mapping to supported effects
                    _bulb.features.effect = True

                self.bulbtype = _bulb
                return _bulb
            raise WizLightNotKnownBulb("The bulb features can not be mapped!")
Esempio n. 3
0
async def test_model_description_light_strip(light_strip: wizlight) -> None:
    """Test fetching the model description for a light strip."""
    bulb_type = await light_strip.get_bulbtype()
    assert bulb_type == BulbType(
        features=Features(
            color=True, color_tmp=True, effect=True, brightness=True, dual_head=False
        ),
        name="ESP03_SHRGB3_01ABI",
        kelvin_range=KelvinRange(max=6500, min=2700),
        bulb_type=BulbClass.RGB,
        fw_version="1.16.64",
        white_channels=1,
        white_to_color_ratio=20,
    )
Esempio n. 4
0
async def test_model_description_rgbww_bulb(rgbww_bulb: wizlight) -> None:
    """Test fetching the model description rgbww bulb."""
    bulb_type = await rgbww_bulb.get_bulbtype()
    assert bulb_type == BulbType(
        features=Features(
            color=True, color_tmp=True, effect=True, brightness=True, dual_head=False
        ),
        name="ESP01_SHRGB1C_31",
        kelvin_range=KelvinRange(max=6500, min=2700),
        bulb_type=BulbClass.RGB,
        fw_version="1.17.1",
        white_channels=2,
        white_to_color_ratio=20,
    )
async def test_model_description_dimmable_bulb(dimmable_bulb: wizlight) -> None:
    """Test fetching the model description dimmable bulb."""
    bulb_type = await dimmable_bulb.get_bulbtype()
    assert bulb_type == BulbType(
        features=Features(
            color=False, color_tmp=False, effect=True, brightness=True, dual_head=False
        ),
        name=None,
        kelvin_range=None,
        bulb_type=BulbClass.DW,
        fw_version="1.8.0",
        white_channels=1,
        white_to_color_ratio=20,
    )
async def test_model_description_wall_switch(wall_switch: wizlight) -> None:
    """Test fetching the model description wall switch."""
    bulb_type = await wall_switch.get_bulbtype()
    assert bulb_type == BulbType(
        features=Features(
            color=False, color_tmp=False, effect=False, brightness=True, dual_head=False
        ),
        name="ESP01_DIMTRIACS_01",
        kelvin_range=KelvinRange(max=2700, min=2700),
        bulb_type=BulbClass.DW,
        fw_version="1.16.68",
        white_channels=1,
        white_to_color_ratio=20,
    )
Esempio n. 7
0
async def test_model_description_squire(squire: wizlight) -> None:
    """Test fetching the model description for a squire."""
    bulb_type = await squire.get_bulbtype()
    assert bulb_type == BulbType(
        features=Features(
            color=True, color_tmp=True, effect=True, brightness=True, dual_head=True
        ),
        name="ESP20_DHRGB_01B",
        kelvin_range=KelvinRange(max=6500, min=2200),
        bulb_type=BulbClass.RGB,
        fw_version="1.21.40",
        white_channels=2,
        white_to_color_ratio=20,
    )
Esempio n. 8
0
async def test_fw_version(correct_bulb: wizlight) -> None:
    """Test fetching the firmware version."""
    bulb_type = await correct_bulb.get_bulbtype()
    assert bulb_type == BulbType(
        features=Features(
            color=True, color_tmp=True, effect=True, brightness=True, dual_head=False
        ),
        name="ESP01_SHRGB_03",
        kelvin_range=KelvinRange(max=6500, min=2200),
        bulb_type=BulbClass.RGB,
        fw_version="1.25.0",
        white_channels=1,
        white_to_color_ratio=30,
    )
    assert correct_bulb.mac == "a8bb5006033d"
async def test_model_description_dimmable_bulb(
        turnable_bulb: wizlight) -> None:
    """Test fetching the model description dimmable bulb."""
    bulb_type = await turnable_bulb.get_bulbtype()
    assert bulb_type == BulbType(
        features=Features(color=False,
                          color_tmp=True,
                          effect=True,
                          brightness=True,
                          dual_head=False),
        name="ESP14_SHTW1C_01",
        kelvin_range=KelvinRange(max=6500, min=2700),
        bulb_type=BulbClass.TW,
        fw_version="1.18.0",
        white_channels=1,
        white_to_color_ratio=20,
    )
Esempio n. 10
0
    async def get_bulbtype(self) -> BulbType:
        """Retrun the bulb type as BulbType object."""
        if self.bulbtype is None:
            bulb_config = await self.getBulbConfig()
            if "moduleName" in bulb_config["result"]:
                _bulbtype = bulb_config["result"]["moduleName"]
                # set the minimum features for dimmable bulbs (DW bulbs)
                # define the kelvin range
                _kelvin = await self.getExtendedWhiteRange()
                # use only first and last entry - [2200,2700,6500,6500]
                _kelvin = _kelvin[:: len(_kelvin) - 1]
                _bulb = BulbType(
                    bulb_type=BulbClass.DW,
                    name=_bulbtype,
                    features=Features(
                        brightness=True, color=False, effect=False, color_tmp=False
                    ),
                    kelvin_range=None,
                )
                try:
                    # parse the features from name
                    _identifier = _bulbtype.split("_")[1]
                # Throw exception if index can not be found
                except IndexError:
                    raise WizLightNotKnownBulb("The bulb type can not be determined!")
                # try to map extensions to the BulbTyp object
                # Color support
                if "RGB" in _identifier:
                    _bulb.kelvin_range = KelvinRange(min=_kelvin[0], max=_kelvin[1])
                    _bulb.bulb_type = BulbClass.RGB
                    _bulb.features.color = True
                    # RGB supports effects and CCT
                    _bulb.features.effect = True
                    _bulb.features.color_tmp = True
                # Tunable white bulb; no RGB
                if "TW" in _identifier:
                    _bulb.kelvin_range = KelvinRange(min=_kelvin[0], max=_kelvin[1])
                    _bulb.kelvin_range = KelvinRange(min=2700, max=6500)
                    _bulb.bulb_type = BulbClass.TW
                    _bulb.features.color_tmp = True
                    # TW supports some effects only
                    # TODO: Improve the mapping to supported effects
                    _bulb.features.effect = True

                self.bulbtype = _bulb
                return _bulb
            raise WizLightNotKnownBulb("The bulb features can not be mapped!")
Esempio n. 11
0
async def test_model_description_socket(socket: wizlight) -> None:
    """Test fetching the model description of a socket is None."""
    bulb_type = await socket.get_bulbtype()
    assert bulb_type == BulbType(
        features=Features(
            color=False,
            color_tmp=False,
            effect=False,
            brightness=False,
            dual_head=False,
        ),
        name="ESP10_SOCKET_06",
        kelvin_range=KelvinRange(max=6500, min=2700),
        bulb_type=BulbClass.SOCKET,
        fw_version="1.16.71",
        white_channels=2,
        white_to_color_ratio=20,
    )
Esempio n. 12
0
async def test_model_description_unknown_bulb(
        unknown_bulb: wizlight, caplog: pytest.LogCaptureFixture) -> None:
    """Test fetching the model description for an unknown bulb."""
    bulb_type = await unknown_bulb.get_bulbtype()
    assert bulb_type == BulbType(
        features=Features(color=False,
                          color_tmp=False,
                          effect=True,
                          brightness=True,
                          dual_head=False),
        name=None,
        kelvin_range=None,
        bulb_type=BulbClass.DW,
        fw_version="1.8.0",
        white_channels=1,
        white_to_color_ratio=20,
    )
    assert (
        "Unknown typeId: 1, please report what kind of bulb this is at https://github.com/sbidy/pywizlight/issues/new"
        in caplog.text)
Esempio n. 13
0
async def test_push_updates(socket_push: wizlight) -> None:
    """Test push updates."""
    bulb_type = await socket_push.get_bulbtype()
    assert bulb_type == BulbType(
        features=Features(
            color=False,
            color_tmp=False,
            effect=False,
            brightness=False,
            dual_head=False,
        ),
        name="ESP10_SOCKET_06",
        kelvin_range=KelvinRange(max=2700, min=2700),
        bulb_type=BulbClass.SOCKET,
        fw_version="1.25.0",
        white_channels=2,
        white_to_color_ratio=20,
    )
    last_data = PilotParser({})
    data_event = asyncio.Event()

    def _on_push(data: PilotParser) -> None:
        nonlocal last_data
        last_data = data
        data_event.set()

    with patch("pywizlight.push_manager.LISTEN_PORT", 0):
        assert await socket_push.start_push(_on_push) is True

    push_manager = PushManager().get()
    push_port = push_manager.push_transport.get_extra_info("sockname")[1]

    push_in_transport_proto = await asyncio.get_event_loop(
    ).create_datagram_endpoint(
        lambda: WizProtocol(on_response=lambda resp, addr: None),
        remote_addr=("127.0.0.1", push_port),
    )
    push_transport = cast(asyncio.DatagramTransport,
                          push_in_transport_proto[0])
    params = {
        "mac": "a8bb5006033d",
        "rssi": -71,
        "src": "hb",
        "mqttCd": 255,
        "ts": 1644593327,
        "pc": 660,
        "state": False,
        "sceneId": 0,
        "temp": 6500,
        "dimming": 100,
    }

    push_transport.sendto(
        to_wiz_json({
            "method": "syncPilot",
            "env": "pro",
            "params": params,
        }).encode(),
        ("127.0.0.1", push_port),
    )
    await asyncio.wait_for(data_event.wait(), timeout=1)
    assert last_data.pilotResult == params
    update = await socket_push.updateState()
    assert update is not None
    assert update.pilotResult == params
    assert await socket_push.get_power() == 0.660

    diagnostics = socket_push.diagnostics
    assert diagnostics["bulb_type"]["bulb_type"] == "SOCKET"
    assert diagnostics["history"]["last_error"] is None
    assert diagnostics["push_running"] is True
    assert (diagnostics["history"]["push"]["syncPilot"]["params"]["mac"] ==
            "a8bb5006033d")
    assert diagnostics["push_manager"]["running"] is True
    assert diagnostics["push_manager"]["fail_reason"] is None

    push_transport.close()
Esempio n. 14
0
async def test_discovery_by_firstbeat(
        socket_push: wizlight, caplog: pytest.LogCaptureFixture) -> None:
    """Test discovery from first beat."""
    bulb_type = await socket_push.get_bulbtype()
    assert bulb_type == BulbType(
        features=Features(
            color=False,
            color_tmp=False,
            effect=False,
            brightness=False,
            dual_head=False,
        ),
        name="ESP10_SOCKET_06",
        kelvin_range=KelvinRange(max=2700, min=2700),
        bulb_type=BulbClass.SOCKET,
        fw_version="1.25.0",
        white_channels=2,
        white_to_color_ratio=20,
    )
    last_discovery: Optional[DiscoveredBulb] = None
    discovery_event = asyncio.Event()

    def _on_discovery(discovery: DiscoveredBulb) -> None:
        nonlocal last_discovery
        last_discovery = discovery
        discovery_event.set()

    with patch("pywizlight.push_manager.LISTEN_PORT", 0):
        assert await socket_push.start_push(lambda data: None) is True

    assert socket_push.mac is not None
    socket_push.set_discovery_callback(_on_discovery)
    push_manager = PushManager().get()
    push_port = push_manager.push_transport.get_extra_info("sockname")[1]

    push_in_transport_proto = await asyncio.get_event_loop(
    ).create_datagram_endpoint(
        lambda: WizProtocol(on_response=lambda resp, addr: None),
        remote_addr=("127.0.0.1", push_port),
    )
    push_transport = cast(asyncio.DatagramTransport,
                          push_in_transport_proto[0])
    push_transport.sendto(
        b"test",
        ("127.0.0.1", push_port),
    )
    push_transport.sendto(
        b"GARBAGE",
        ("127.0.0.1", push_port),
    )
    push_transport.sendto(
        to_wiz_json({
            "method": "firstBeat",
            "env": "pro",
            "params": {
                "mac": socket_push.mac
            },
        }).encode(),
        ("127.0.0.1", push_port),
    )
    await asyncio.wait_for(discovery_event.wait(), timeout=1)
    assert last_discovery is not None
    assert last_discovery == DiscoveredBulb("127.0.0.1", socket_push.mac)
    push_transport.close()
    assert "GARBAGE" in caplog.text