Example #1
0
 async def test__state_change_removed(self, mock_device: Device):
     """Test that service information are not processed on state change to removed."""
     with patch("devolo_plc_api.device.Device._retry_zeroconf_info"), patch(
             "devolo_plc_api.device.Device._get_service_info") as gsi:
         mock_device._state_change(Mock(), PLCNETAPI, PLCNETAPI,
                                   ServiceStateChange.Removed)
         assert gsi.call_count == 0
Example #2
0
 async def test__not_get_plcnet_info(self, mock_device: Device,
                                     device_api: DeviceApi):
     """Test that devices know to not have a plcnet API don't query it."""
     mock_device.mt_number = DEVICES_WITHOUT_PLCNET[0]
     with patch("devolo_plc_api.device.Device._get_zeroconf_info"
                ) as gzi, patch(
                    "devolo_plc_api.device.Device._get_device_info"):
         mock_device.device = device_api
         await mock_device.async_connect()
         assert gzi.call_count == 0
         assert not mock_device.plcnet
Example #3
0
    async def test_async_connect(self, mock_device: Device,
                                 device_api: DeviceApi, plcnet_api: PlcNetApi):
        """Test that connecting to a device calls methos to collect information from the APIs."""
        with patch(
                "devolo_plc_api.device.Device._get_device_info") as gdi, patch(
                    "devolo_plc_api.device.Device._get_plcnet_info") as gpi:
            mock_device.device = device_api
            mock_device.plcnet = plcnet_api
            await mock_device.async_connect()
            assert gdi.call_count == 1
            assert gpi.call_count == 1
            assert getattr(mock_device, "_connected")

            await mock_device.async_connect(session_instance=AsyncMock())
            assert gdi.call_count == 2
            assert gpi.call_count == 2
            assert getattr(mock_device, "_connected")
Example #4
0
 async def test__get_plcnet_info_multicast(self, test_data: TestData):
     """Test that devices having trouble with unicast zeroconf are queried twice."""
     with patch("devolo_plc_api.device.Device._get_zeroconf_info"
                ) as gzi, pytest.raises(DeviceNotFound):
         device = Device(test_data.ip)
         await device.async_connect()
         assert getattr(device, "_multicast")
         assert gzi.call_count == 2
Example #5
0
 async def test__get_zeroconf_info(
         self, test_data: TestData,
         mock_service_browser: MockServiceBrowser):
     """Test that after getting zeroconf information browsing is stopped."""
     with patch("devolo_plc_api.device.Device._state_change", state_change):
         mock_device = Device(ip=test_data.ip,
                              plcnetapi=None,
                              deviceapi=test_data.device_info[DEVICEAPI])
         await mock_device.async_connect()
         assert mock_service_browser.async_cancel.call_count == 1
Example #6
0
 async def test__state_change_added(self, test_data: TestData):
     """Test that service information are processed on state change to added."""
     with patch("devolo_plc_api.device.Device._get_service_info"
                ) as gsi, patch(
                    "devolo_plc_api.device.Device._retry_zeroconf_info"
                ), patch("asyncio.sleep"):
         mock_device = Device(ip=test_data.ip,
                              plcnetapi=None,
                              deviceapi=test_data.device_info[DEVICEAPI])
         await mock_device.async_connect()
         assert gsi.call_count == 1
Example #7
0
 async def test__state_change_no_service_info(self, test_data: TestData):
     """Test that waiting for mDNS responses continues, if no service info were received."""
     with patch("devolo_plc_api.device.Device.info_from_service"
                ) as ifs, patch(
                    "devolo_plc_api.device.AsyncServiceInfo.async_request"
                ), patch("asyncio.sleep"):
         mock_device = Device(ip=test_data.ip,
                              plcnetapi=None,
                              deviceapi=test_data.device_info[DEVICEAPI])
         await mock_device.async_connect()
         assert ifs.call_count == 0
Example #8
0
 async def test__get_service_info(self, test_data: TestData):
     """Test storing of information discovered via mDNS."""
     with patch(
             "devolo_plc_api.device.AsyncServiceInfo", StubAsyncServiceInfo
     ), patch("devolo_plc_api.device.PlcNetApi"), patch(
             "devolo_plc_api.device.AsyncServiceInfo.async_request") as ar:
         mock_device = Device(ip=test_data.ip,
                              plcnetapi=None,
                              deviceapi=test_data.device_info[DEVICEAPI])
         await mock_device.async_connect()
         assert ar.call_count == 1
         assert mock_device.mac == test_data.device_info[PLCNETAPI][
             "properties"]["PlcMacAddress"]
Example #9
0
async def validate_input(hass: core.HomeAssistant,
                         data: dict[str, Any]) -> dict[str, str]:
    """Validate the user input allows us to connect.

    Data has the keys from STEP_USER_DATA_SCHEMA with values provided by the user.
    """
    zeroconf_instance = await zeroconf.async_get_instance(hass)
    async_client = get_async_client(hass)

    device = Device(data[CONF_IP_ADDRESS], zeroconf_instance=zeroconf_instance)

    await device.async_connect(session_instance=async_client)
    await device.async_disconnect()

    return {
        SERIAL_NUMBER: str(device.serial_number),
        TITLE: device.hostname.split(".")[0],
    }
Example #10
0
    async def test__async_wrong_password_type(self, httpx_mock: HTTPXMock,
                                              mock_device: Device):
        """Test using different password hash if original password failed."""
        await mock_device.async_connect()
        assert mock_device.device
        mock_device.password = "******"

        httpx_mock.add_response(status_code=HTTPStatus.UNAUTHORIZED)
        with pytest.raises(DevicePasswordProtected):
            await mock_device.device.async_get_wifi_connected_station()
            assert mock_device.device.password == "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8"

        httpx_mock.add_response(status_code=HTTPStatus.UNAUTHORIZED)
        with pytest.raises(DevicePasswordProtected):
            await mock_device.device.async_get_wifi_connected_station()
            assert mock_device.device.password == "113459eb7bb31bddee85ade5230d6ad5d8b2fb52879e00a84ff6ae1067a210d3"

        await mock_device.async_disconnect()
Example #11
0
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
    """Set up devolo Home Network from a config entry."""
    hass.data.setdefault(DOMAIN, {})
    zeroconf_instance = await zeroconf.async_get_async_instance(hass)
    async_client = get_async_client(hass)

    try:
        device = Device(ip=entry.data[CONF_IP_ADDRESS],
                        zeroconf_instance=zeroconf_instance)
        await device.async_connect(session_instance=async_client)
    except DeviceNotFound as err:
        raise ConfigEntryNotReady(
            f"Unable to connect to {entry.data[CONF_IP_ADDRESS]}") from err

    async def async_update_connected_plc_devices() -> dict[str, Any]:
        """Fetch data from API endpoint."""
        try:
            async with async_timeout.timeout(10):
                return await device.plcnet.async_get_network_overview(
                )  # type: ignore[no-any-return, union-attr]
        except DeviceUnavailable as err:
            raise UpdateFailed(err) from err

    async def async_update_wifi_connected_station() -> dict[str, Any]:
        """Fetch data from API endpoint."""
        try:
            async with async_timeout.timeout(10):
                return await device.device.async_get_wifi_connected_station(
                )  # type: ignore[no-any-return, union-attr]
        except DeviceUnavailable as err:
            raise UpdateFailed(err) from err

    async def async_update_wifi_neighbor_access_points() -> dict[str, Any]:
        """Fetch data from API endpoint."""
        try:
            async with async_timeout.timeout(30):
                return await device.device.async_get_wifi_neighbor_access_points(
                )  # type: ignore[no-any-return, union-attr]
        except DeviceUnavailable as err:
            raise UpdateFailed(err) from err

    async def disconnect(event: Event) -> None:
        """Disconnect from device."""
        await device.async_disconnect()

    coordinators: dict[str, DataUpdateCoordinator] = {}
    if device.plcnet:
        coordinators[CONNECTED_PLC_DEVICES] = DataUpdateCoordinator(
            hass,
            _LOGGER,
            name=CONNECTED_PLC_DEVICES,
            update_method=async_update_connected_plc_devices,
            update_interval=LONG_UPDATE_INTERVAL,
        )
    if device.device and "wifi1" in device.device.features:
        coordinators[CONNECTED_WIFI_CLIENTS] = DataUpdateCoordinator(
            hass,
            _LOGGER,
            name=CONNECTED_WIFI_CLIENTS,
            update_method=async_update_wifi_connected_station,
            update_interval=SHORT_UPDATE_INTERVAL,
        )
        coordinators[NEIGHBORING_WIFI_NETWORKS] = DataUpdateCoordinator(
            hass,
            _LOGGER,
            name=NEIGHBORING_WIFI_NETWORKS,
            update_method=async_update_wifi_neighbor_access_points,
            update_interval=LONG_UPDATE_INTERVAL,
        )

    hass.data[DOMAIN][entry.entry_id] = {
        "device": device,
        "coordinators": coordinators
    }

    for coordinator in coordinators.values():
        await coordinator.async_config_entry_first_refresh()

    await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

    entry.async_on_unload(
        hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, disconnect))

    return True
Example #12
0
 async def test_async_context_manager(self, test_data: TestData):
     """Test the async context manager."""
     with patch("devolo_plc_api.device.Device._state_change", state_change):
         async with Device(test_data.ip) as device:
             assert getattr(device, "_connected")
         assert not getattr(device, "_connected")
Example #13
0
 def test_disconnect(self, mock_device: Device):
     """Test that the sync disconnect method just calls the async disconnect method."""
     with patch("devolo_plc_api.device.Device.async_disconnect",
                new=AsyncMock()) as ad:
         mock_device.disconnect()
         assert ad.call_count == 1
Example #14
0
 def test_info_from_service_no_address(self, mock_device: Device):
     """Test ignoring information received for an other address."""
     service_info = Mock()
     service_info.addresses = None
     assert mock_device.info_from_service(service_info) == {}
Example #15
0
 def test_set_password(self, mock_device: Device, device_api: DeviceApi):
     """Test setting a device password is also reflected in the device API."""
     mock_device.device = device_api
     mock_device.password = "******"
     assert mock_device.device.password == "super_secret"