mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +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/free_mobile/notify.py
|
||||
homeassistant/components/freebox/camera.py
|
||||
homeassistant/components/freebox/device_tracker.py
|
||||
homeassistant/components/freebox/home_base.py
|
||||
homeassistant/components/freebox/router.py
|
||||
homeassistant/components/freebox/switch.py
|
||||
homeassistant/components/fritz/common.py
|
||||
homeassistant/components/fritz/device_tracker.py
|
||||
|
@ -4,9 +4,11 @@ from __future__ import annotations
|
||||
from collections.abc import Mapping
|
||||
from contextlib import suppress
|
||||
from datetime import datetime
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
from typing import Any
|
||||
|
||||
from freebox_api import Freepybox
|
||||
@ -36,6 +38,20 @@ from .const import (
|
||||
_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:
|
||||
"""Get the Freebox API."""
|
||||
freebox_path = Store(hass, STORAGE_VERSION, STORAGE_KEY).path
|
||||
@ -69,6 +85,7 @@ class FreeboxRouter:
|
||||
self._sw_v: str = freebox_config["firmware_version"]
|
||||
self._attrs: dict[str, Any] = {}
|
||||
|
||||
self.supports_hosts = True
|
||||
self.devices: dict[str, dict[str, Any]] = {}
|
||||
self.disks: dict[int, dict[str, Any]] = {}
|
||||
self.supports_raid = True
|
||||
@ -89,7 +106,32 @@ class FreeboxRouter:
|
||||
async def update_device_trackers(self) -> None:
|
||||
"""Update Freebox devices."""
|
||||
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
|
||||
fbx_devices.append(
|
||||
|
@ -1,6 +1,8 @@
|
||||
"""Test helpers for Freebox."""
|
||||
import json
|
||||
from unittest.mock import AsyncMock, PropertyMock, patch
|
||||
|
||||
from freebox_api.exceptions import HttpRequestError
|
||||
import pytest
|
||||
|
||||
from homeassistant.core import HomeAssistant
|
||||
@ -12,6 +14,7 @@ from .const import (
|
||||
DATA_HOME_GET_NODES,
|
||||
DATA_HOME_PIR_GET_VALUES,
|
||||
DATA_LAN_GET_HOSTS_LIST,
|
||||
DATA_LAN_GET_HOSTS_LIST_MODE_BRIDGE,
|
||||
DATA_STORAGE_GET_DISKS,
|
||||
DATA_STORAGE_GET_RAIDS,
|
||||
DATA_SYSTEM_GET_CONFIG,
|
||||
@ -41,7 +44,9 @@ def enable_all_entities():
|
||||
|
||||
|
||||
@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."""
|
||||
config_entry = MockConfigEntry(domain="something_else")
|
||||
config_entry.add_to_hass(hass)
|
||||
@ -87,3 +92,17 @@ def mock_router(mock_device_registry_devices):
|
||||
)
|
||||
instance.close = AsyncMock()
|
||||
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
|
||||
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
|
||||
# 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