mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
Fix integration failed when freebox is configured in bridge mode (#103221)
This commit is contained in:
parent
c2e81bbafb
commit
9c86adf644
@ -425,9 +425,7 @@ omit =
|
|||||||
homeassistant/components/foursquare/*
|
homeassistant/components/foursquare/*
|
||||||
homeassistant/components/free_mobile/notify.py
|
homeassistant/components/free_mobile/notify.py
|
||||||
homeassistant/components/freebox/camera.py
|
homeassistant/components/freebox/camera.py
|
||||||
homeassistant/components/freebox/device_tracker.py
|
|
||||||
homeassistant/components/freebox/home_base.py
|
homeassistant/components/freebox/home_base.py
|
||||||
homeassistant/components/freebox/router.py
|
|
||||||
homeassistant/components/freebox/switch.py
|
homeassistant/components/freebox/switch.py
|
||||||
homeassistant/components/fritz/common.py
|
homeassistant/components/fritz/common.py
|
||||||
homeassistant/components/fritz/device_tracker.py
|
homeassistant/components/fritz/device_tracker.py
|
||||||
|
@ -4,9 +4,11 @@ from __future__ import annotations
|
|||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import re
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from freebox_api import Freepybox
|
from freebox_api import Freepybox
|
||||||
@ -36,6 +38,20 @@ from .const import (
|
|||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def is_json(json_str):
|
||||||
|
"""Validate if a String is a JSON value or not."""
|
||||||
|
try:
|
||||||
|
json.loads(json_str)
|
||||||
|
return True
|
||||||
|
except (ValueError, TypeError) as err:
|
||||||
|
_LOGGER.error(
|
||||||
|
"Failed to parse JSON '%s', error '%s'",
|
||||||
|
json_str,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
async def get_api(hass: HomeAssistant, host: str) -> Freepybox:
|
async def get_api(hass: HomeAssistant, host: str) -> Freepybox:
|
||||||
"""Get the Freebox API."""
|
"""Get the Freebox API."""
|
||||||
freebox_path = Store(hass, STORAGE_VERSION, STORAGE_KEY).path
|
freebox_path = Store(hass, STORAGE_VERSION, STORAGE_KEY).path
|
||||||
@ -69,6 +85,7 @@ class FreeboxRouter:
|
|||||||
self._sw_v: str = freebox_config["firmware_version"]
|
self._sw_v: str = freebox_config["firmware_version"]
|
||||||
self._attrs: dict[str, Any] = {}
|
self._attrs: dict[str, Any] = {}
|
||||||
|
|
||||||
|
self.supports_hosts = True
|
||||||
self.devices: dict[str, dict[str, Any]] = {}
|
self.devices: dict[str, dict[str, Any]] = {}
|
||||||
self.disks: dict[int, dict[str, Any]] = {}
|
self.disks: dict[int, dict[str, Any]] = {}
|
||||||
self.supports_raid = True
|
self.supports_raid = True
|
||||||
@ -89,7 +106,32 @@ class FreeboxRouter:
|
|||||||
async def update_device_trackers(self) -> None:
|
async def update_device_trackers(self) -> None:
|
||||||
"""Update Freebox devices."""
|
"""Update Freebox devices."""
|
||||||
new_device = False
|
new_device = False
|
||||||
fbx_devices: list[dict[str, Any]] = await self._api.lan.get_hosts_list()
|
|
||||||
|
fbx_devices: list[dict[str, Any]] = []
|
||||||
|
|
||||||
|
# Access to Host list not available in bridge mode, API return error_code 'nodev'
|
||||||
|
if self.supports_hosts:
|
||||||
|
try:
|
||||||
|
fbx_devices = await self._api.lan.get_hosts_list()
|
||||||
|
except HttpRequestError as err:
|
||||||
|
if (
|
||||||
|
(
|
||||||
|
matcher := re.search(
|
||||||
|
r"Request failed \(APIResponse: (.+)\)", str(err)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
and is_json(json_str := matcher.group(1))
|
||||||
|
and (json_resp := json.loads(json_str)).get("error_code") == "nodev"
|
||||||
|
):
|
||||||
|
# No need to retry, Host list not available
|
||||||
|
self.supports_hosts = False
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Host list is not available using bridge mode (%s)",
|
||||||
|
json_resp.get("msg"),
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise err
|
||||||
|
|
||||||
# Adds the Freebox itself
|
# Adds the Freebox itself
|
||||||
fbx_devices.append(
|
fbx_devices.append(
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
"""Test helpers for Freebox."""
|
"""Test helpers for Freebox."""
|
||||||
|
import json
|
||||||
from unittest.mock import AsyncMock, PropertyMock, patch
|
from unittest.mock import AsyncMock, PropertyMock, patch
|
||||||
|
|
||||||
|
from freebox_api.exceptions import HttpRequestError
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from homeassistant.core import HomeAssistant
|
from homeassistant.core import HomeAssistant
|
||||||
@ -12,6 +14,7 @@ from .const import (
|
|||||||
DATA_HOME_GET_NODES,
|
DATA_HOME_GET_NODES,
|
||||||
DATA_HOME_PIR_GET_VALUES,
|
DATA_HOME_PIR_GET_VALUES,
|
||||||
DATA_LAN_GET_HOSTS_LIST,
|
DATA_LAN_GET_HOSTS_LIST,
|
||||||
|
DATA_LAN_GET_HOSTS_LIST_MODE_BRIDGE,
|
||||||
DATA_STORAGE_GET_DISKS,
|
DATA_STORAGE_GET_DISKS,
|
||||||
DATA_STORAGE_GET_RAIDS,
|
DATA_STORAGE_GET_RAIDS,
|
||||||
DATA_SYSTEM_GET_CONFIG,
|
DATA_SYSTEM_GET_CONFIG,
|
||||||
@ -41,7 +44,9 @@ def enable_all_entities():
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def mock_device_registry_devices(hass: HomeAssistant, device_registry):
|
def mock_device_registry_devices(
|
||||||
|
hass: HomeAssistant, device_registry: dr.DeviceRegistry
|
||||||
|
):
|
||||||
"""Create device registry devices so the device tracker entities are enabled."""
|
"""Create device registry devices so the device tracker entities are enabled."""
|
||||||
config_entry = MockConfigEntry(domain="something_else")
|
config_entry = MockConfigEntry(domain="something_else")
|
||||||
config_entry.add_to_hass(hass)
|
config_entry.add_to_hass(hass)
|
||||||
@ -87,3 +92,17 @@ def mock_router(mock_device_registry_devices):
|
|||||||
)
|
)
|
||||||
instance.close = AsyncMock()
|
instance.close = AsyncMock()
|
||||||
yield service_mock
|
yield service_mock
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="router_bridge_mode")
|
||||||
|
def mock_router_bridge_mode(mock_device_registry_devices, router):
|
||||||
|
"""Mock a successful connection to Freebox Bridge mode."""
|
||||||
|
|
||||||
|
router().lan.get_hosts_list = AsyncMock(
|
||||||
|
side_effect=HttpRequestError(
|
||||||
|
"Request failed (APIResponse: %s)"
|
||||||
|
% json.dumps(DATA_LAN_GET_HOSTS_LIST_MODE_BRIDGE)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return router
|
||||||
|
@ -25,7 +25,9 @@ WIFI_GET_GLOBAL_CONFIG = load_json_object_fixture("freebox/wifi_get_global_confi
|
|||||||
|
|
||||||
# device_tracker
|
# device_tracker
|
||||||
DATA_LAN_GET_HOSTS_LIST = load_json_array_fixture("freebox/lan_get_hosts_list.json")
|
DATA_LAN_GET_HOSTS_LIST = load_json_array_fixture("freebox/lan_get_hosts_list.json")
|
||||||
|
DATA_LAN_GET_HOSTS_LIST_MODE_BRIDGE = load_json_object_fixture(
|
||||||
|
"freebox/lan_get_hosts_list_bridge.json"
|
||||||
|
)
|
||||||
|
|
||||||
# Home
|
# Home
|
||||||
# ALL
|
# ALL
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"msg": "Erreur lors de la récupération de la liste des hôtes : Interface invalide",
|
||||||
|
"success": false,
|
||||||
|
"error_code": "nodev"
|
||||||
|
}
|
49
tests/components/freebox/test_device_tracker.py
Normal file
49
tests/components/freebox/test_device_tracker.py
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
"""Tests for the Freebox device trackers."""
|
||||||
|
from unittest.mock import Mock
|
||||||
|
|
||||||
|
from freezegun.api import FrozenDateTimeFactory
|
||||||
|
|
||||||
|
from homeassistant.components.device_tracker import DOMAIN as DEVICE_TRACKER_DOMAIN
|
||||||
|
from homeassistant.components.freebox import SCAN_INTERVAL
|
||||||
|
from homeassistant.core import HomeAssistant
|
||||||
|
|
||||||
|
from .common import setup_platform
|
||||||
|
|
||||||
|
from tests.common import async_fire_time_changed
|
||||||
|
|
||||||
|
|
||||||
|
async def test_router_mode(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
freezer: FrozenDateTimeFactory,
|
||||||
|
router: Mock,
|
||||||
|
) -> None:
|
||||||
|
"""Test get_hosts_list invoqued multiple times if freebox into router mode."""
|
||||||
|
await setup_platform(hass, DEVICE_TRACKER_DOMAIN)
|
||||||
|
|
||||||
|
assert router().lan.get_hosts_list.call_count == 1
|
||||||
|
|
||||||
|
# Simulate an update
|
||||||
|
freezer.tick(SCAN_INTERVAL)
|
||||||
|
async_fire_time_changed(hass)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
assert router().lan.get_hosts_list.call_count == 2
|
||||||
|
|
||||||
|
|
||||||
|
async def test_bridge_mode(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
freezer: FrozenDateTimeFactory,
|
||||||
|
router_bridge_mode: Mock,
|
||||||
|
) -> None:
|
||||||
|
"""Test get_hosts_list invoqued once if freebox into bridge mode."""
|
||||||
|
await setup_platform(hass, DEVICE_TRACKER_DOMAIN)
|
||||||
|
|
||||||
|
assert router_bridge_mode().lan.get_hosts_list.call_count == 1
|
||||||
|
|
||||||
|
# Simulate an update
|
||||||
|
freezer.tick(SCAN_INTERVAL)
|
||||||
|
async_fire_time_changed(hass)
|
||||||
|
await hass.async_block_till_done()
|
||||||
|
|
||||||
|
# If get_hosts_list failed, not called again
|
||||||
|
assert router_bridge_mode().lan.get_hosts_list.call_count == 1
|
22
tests/components/freebox/test_router.py
Normal file
22
tests/components/freebox/test_router.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
"""Tests for the Freebox utility methods."""
|
||||||
|
import json
|
||||||
|
|
||||||
|
from homeassistant.components.freebox.router import is_json
|
||||||
|
|
||||||
|
from .const import DATA_LAN_GET_HOSTS_LIST_MODE_BRIDGE, WIFI_GET_GLOBAL_CONFIG
|
||||||
|
|
||||||
|
|
||||||
|
async def test_is_json() -> None:
|
||||||
|
"""Test is_json method."""
|
||||||
|
|
||||||
|
# Valid JSON values
|
||||||
|
assert is_json("{}")
|
||||||
|
assert is_json('{ "simple":"json" }')
|
||||||
|
assert is_json(json.dumps(WIFI_GET_GLOBAL_CONFIG))
|
||||||
|
assert is_json(json.dumps(DATA_LAN_GET_HOSTS_LIST_MODE_BRIDGE))
|
||||||
|
|
||||||
|
# Not valid JSON values
|
||||||
|
assert not is_json(None)
|
||||||
|
assert not is_json("")
|
||||||
|
assert not is_json("XXX")
|
||||||
|
assert not is_json("{XXX}")
|
Loading…
x
Reference in New Issue
Block a user