Bump sfrbox-api to 0.0.10 (#125405)
* bump sfr_box requirement to 0.0.10 * upate manifest file * Handle None values --------- Co-authored-by: epenet <6771947+epenet@users.noreply.github.com>pull/125421/head
parent
e58cf00a96
commit
c4cc158a77
|
@ -3,6 +3,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from sfrbox_api.bridge import SFRBox
|
from sfrbox_api.bridge import SFRBox
|
||||||
from sfrbox_api.exceptions import SFRBoxAuthenticationError, SFRBoxError
|
from sfrbox_api.exceptions import SFRBoxAuthenticationError, SFRBoxError
|
||||||
|
@ -46,6 +47,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||||
# Preload system information
|
# Preload system information
|
||||||
await data.system.async_config_entry_first_refresh()
|
await data.system.async_config_entry_first_refresh()
|
||||||
system_info = data.system.data
|
system_info = data.system.data
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
assert system_info is not None
|
||||||
|
|
||||||
# Preload other coordinators (based on net infrastructure)
|
# Preload other coordinators (based on net infrastructure)
|
||||||
tasks = [data.wan.async_config_entry_first_refresh()]
|
tasks = [data.wan.async_config_entry_first_refresh()]
|
||||||
|
|
|
@ -4,6 +4,7 @@ from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from sfrbox_api.models import DslInfo, FtthInfo, SystemInfo, WanInfo
|
from sfrbox_api.models import DslInfo, FtthInfo, SystemInfo, WanInfo
|
||||||
|
|
||||||
|
@ -65,19 +66,22 @@ async def async_setup_entry(
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the sensors."""
|
"""Set up the sensors."""
|
||||||
data: DomainData = hass.data[DOMAIN][entry.entry_id]
|
data: DomainData = hass.data[DOMAIN][entry.entry_id]
|
||||||
|
system_info = data.system.data
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
assert system_info is not None
|
||||||
|
|
||||||
entities: list[SFRBoxBinarySensor] = [
|
entities: list[SFRBoxBinarySensor] = [
|
||||||
SFRBoxBinarySensor(data.wan, description, data.system.data)
|
SFRBoxBinarySensor(data.wan, description, system_info)
|
||||||
for description in WAN_SENSOR_TYPES
|
for description in WAN_SENSOR_TYPES
|
||||||
]
|
]
|
||||||
if (net_infra := data.system.data.net_infra) == "adsl":
|
if (net_infra := system_info.net_infra) == "adsl":
|
||||||
entities.extend(
|
entities.extend(
|
||||||
SFRBoxBinarySensor(data.dsl, description, data.system.data)
|
SFRBoxBinarySensor(data.dsl, description, system_info)
|
||||||
for description in DSL_SENSOR_TYPES
|
for description in DSL_SENSOR_TYPES
|
||||||
)
|
)
|
||||||
elif net_infra == "ftth":
|
elif net_infra == "ftth":
|
||||||
entities.extend(
|
entities.extend(
|
||||||
SFRBoxBinarySensor(data.ftth, description, data.system.data)
|
SFRBoxBinarySensor(data.ftth, description, system_info)
|
||||||
for description in FTTH_SENSOR_TYPES
|
for description in FTTH_SENSOR_TYPES
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -111,4 +115,6 @@ class SFRBoxBinarySensor[_T](
|
||||||
@property
|
@property
|
||||||
def is_on(self) -> bool | None:
|
def is_on(self) -> bool | None:
|
||||||
"""Return the native value of the device."""
|
"""Return the native value of the device."""
|
||||||
|
if self.coordinator.data is None:
|
||||||
|
return None
|
||||||
return self.entity_description.value_fn(self.coordinator.data)
|
return self.entity_description.value_fn(self.coordinator.data)
|
||||||
|
|
|
@ -5,7 +5,7 @@ from __future__ import annotations
|
||||||
from collections.abc import Awaitable, Callable, Coroutine
|
from collections.abc import Awaitable, Callable, Coroutine
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from typing import Any, Concatenate
|
from typing import TYPE_CHECKING, Any, Concatenate
|
||||||
|
|
||||||
from sfrbox_api.bridge import SFRBox
|
from sfrbox_api.bridge import SFRBox
|
||||||
from sfrbox_api.exceptions import SFRBoxError
|
from sfrbox_api.exceptions import SFRBoxError
|
||||||
|
@ -69,10 +69,12 @@ async def async_setup_entry(
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the buttons."""
|
"""Set up the buttons."""
|
||||||
data: DomainData = hass.data[DOMAIN][entry.entry_id]
|
data: DomainData = hass.data[DOMAIN][entry.entry_id]
|
||||||
|
system_info = data.system.data
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
assert system_info is not None
|
||||||
|
|
||||||
entities = [
|
entities = [
|
||||||
SFRBoxButton(data.box, description, data.system.data)
|
SFRBoxButton(data.box, description, system_info) for description in BUTTON_TYPES
|
||||||
for description in BUTTON_TYPES
|
|
||||||
]
|
]
|
||||||
async_add_entities(entities)
|
async_add_entities(entities)
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from collections.abc import Mapping
|
from collections.abc import Mapping
|
||||||
from typing import Any
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
from sfrbox_api.bridge import SFRBox
|
from sfrbox_api.bridge import SFRBox
|
||||||
from sfrbox_api.exceptions import SFRBoxAuthenticationError, SFRBoxError
|
from sfrbox_api.exceptions import SFRBoxAuthenticationError, SFRBoxError
|
||||||
|
@ -51,6 +51,8 @@ class SFRBoxFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||||
except SFRBoxError:
|
except SFRBoxError:
|
||||||
errors["base"] = "cannot_connect"
|
errors["base"] = "cannot_connect"
|
||||||
else:
|
else:
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
assert system_info is not None
|
||||||
await self.async_set_unique_id(system_info.mac_addr)
|
await self.async_set_unique_id(system_info.mac_addr)
|
||||||
self._abort_if_unique_id_configured()
|
self._abort_if_unique_id_configured()
|
||||||
self._async_abort_entries_match({CONF_HOST: user_input[CONF_HOST]})
|
self._async_abort_entries_match({CONF_HOST: user_input[CONF_HOST]})
|
||||||
|
|
|
@ -15,7 +15,7 @@ _LOGGER = logging.getLogger(__name__)
|
||||||
_SCAN_INTERVAL = timedelta(minutes=1)
|
_SCAN_INTERVAL = timedelta(minutes=1)
|
||||||
|
|
||||||
|
|
||||||
class SFRDataUpdateCoordinator[_DataT](DataUpdateCoordinator[_DataT]):
|
class SFRDataUpdateCoordinator[_DataT](DataUpdateCoordinator[_DataT | None]):
|
||||||
"""Coordinator to manage data updates."""
|
"""Coordinator to manage data updates."""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
@ -23,14 +23,14 @@ class SFRDataUpdateCoordinator[_DataT](DataUpdateCoordinator[_DataT]):
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
box: SFRBox,
|
box: SFRBox,
|
||||||
name: str,
|
name: str,
|
||||||
method: Callable[[SFRBox], Coroutine[Any, Any, _DataT]],
|
method: Callable[[SFRBox], Coroutine[Any, Any, _DataT | None]],
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize coordinator."""
|
"""Initialize coordinator."""
|
||||||
self.box = box
|
self.box = box
|
||||||
self._method = method
|
self._method = method
|
||||||
super().__init__(hass, _LOGGER, name=name, update_interval=_SCAN_INTERVAL)
|
super().__init__(hass, _LOGGER, name=name, update_interval=_SCAN_INTERVAL)
|
||||||
|
|
||||||
async def _async_update_data(self) -> _DataT:
|
async def _async_update_data(self) -> _DataT | None:
|
||||||
"""Update data."""
|
"""Update data."""
|
||||||
try:
|
try:
|
||||||
return await self._method(self.box)
|
return await self._method(self.box)
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import dataclasses
|
import dataclasses
|
||||||
from typing import Any
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
from homeassistant.components.diagnostics import async_redact_data
|
from homeassistant.components.diagnostics import async_redact_data
|
||||||
from homeassistant.config_entries import ConfigEntry
|
from homeassistant.config_entries import ConfigEntry
|
||||||
|
@ -12,9 +12,18 @@ from homeassistant.core import HomeAssistant
|
||||||
from .const import DOMAIN
|
from .const import DOMAIN
|
||||||
from .models import DomainData
|
from .models import DomainData
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from _typeshed import DataclassInstance
|
||||||
|
|
||||||
TO_REDACT = {"mac_addr", "serial_number", "ip_addr", "ipv6_addr"}
|
TO_REDACT = {"mac_addr", "serial_number", "ip_addr", "ipv6_addr"}
|
||||||
|
|
||||||
|
|
||||||
|
def _async_redact_data(obj: DataclassInstance | None) -> dict[str, Any] | None:
|
||||||
|
if obj is None:
|
||||||
|
return None
|
||||||
|
return async_redact_data(dataclasses.asdict(obj), TO_REDACT)
|
||||||
|
|
||||||
|
|
||||||
async def async_get_config_entry_diagnostics(
|
async def async_get_config_entry_diagnostics(
|
||||||
hass: HomeAssistant, entry: ConfigEntry
|
hass: HomeAssistant, entry: ConfigEntry
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
|
@ -27,21 +36,9 @@ async def async_get_config_entry_diagnostics(
|
||||||
"data": dict(entry.data),
|
"data": dict(entry.data),
|
||||||
},
|
},
|
||||||
"data": {
|
"data": {
|
||||||
"dsl": async_redact_data(
|
"dsl": _async_redact_data(await data.system.box.dsl_get_info()),
|
||||||
dataclasses.asdict(await data.system.box.dsl_get_info()),
|
"ftth": _async_redact_data(await data.system.box.ftth_get_info()),
|
||||||
TO_REDACT,
|
"system": _async_redact_data(await data.system.box.system_get_info()),
|
||||||
),
|
"wan": _async_redact_data(await data.system.box.wan_get_info()),
|
||||||
"ftth": async_redact_data(
|
|
||||||
dataclasses.asdict(await data.system.box.ftth_get_info()),
|
|
||||||
TO_REDACT,
|
|
||||||
),
|
|
||||||
"system": async_redact_data(
|
|
||||||
dataclasses.asdict(await data.system.box.system_get_info()),
|
|
||||||
TO_REDACT,
|
|
||||||
),
|
|
||||||
"wan": async_redact_data(
|
|
||||||
dataclasses.asdict(await data.system.box.wan_get_info()),
|
|
||||||
TO_REDACT,
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,5 +6,5 @@
|
||||||
"documentation": "https://www.home-assistant.io/integrations/sfr_box",
|
"documentation": "https://www.home-assistant.io/integrations/sfr_box",
|
||||||
"integration_type": "device",
|
"integration_type": "device",
|
||||||
"iot_class": "local_polling",
|
"iot_class": "local_polling",
|
||||||
"requirements": ["sfrbox-api==0.0.8"]
|
"requirements": ["sfrbox-api==0.0.10"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from sfrbox_api.models import DslInfo, SystemInfo, WanInfo
|
from sfrbox_api.models import DslInfo, SystemInfo, WanInfo
|
||||||
|
|
||||||
|
@ -129,7 +130,7 @@ DSL_SENSOR_TYPES: tuple[SFRBoxSensorEntityDescription[DslInfo], ...] = (
|
||||||
"unknown",
|
"unknown",
|
||||||
],
|
],
|
||||||
translation_key="dsl_line_status",
|
translation_key="dsl_line_status",
|
||||||
value_fn=lambda x: x.line_status.lower().replace(" ", "_"),
|
value_fn=lambda x: _value_to_option(x.line_status),
|
||||||
),
|
),
|
||||||
SFRBoxSensorEntityDescription[DslInfo](
|
SFRBoxSensorEntityDescription[DslInfo](
|
||||||
key="training",
|
key="training",
|
||||||
|
@ -149,7 +150,7 @@ DSL_SENSOR_TYPES: tuple[SFRBoxSensorEntityDescription[DslInfo], ...] = (
|
||||||
"unknown",
|
"unknown",
|
||||||
],
|
],
|
||||||
translation_key="dsl_training",
|
translation_key="dsl_training",
|
||||||
value_fn=lambda x: x.training.lower().replace(" ", "_").replace(".", "_"),
|
value_fn=lambda x: _value_to_option(x.training),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
SYSTEM_SENSOR_TYPES: tuple[SFRBoxSensorEntityDescription[SystemInfo], ...] = (
|
SYSTEM_SENSOR_TYPES: tuple[SFRBoxSensorEntityDescription[SystemInfo], ...] = (
|
||||||
|
@ -181,7 +182,7 @@ SYSTEM_SENSOR_TYPES: tuple[SFRBoxSensorEntityDescription[SystemInfo], ...] = (
|
||||||
entity_category=EntityCategory.DIAGNOSTIC,
|
entity_category=EntityCategory.DIAGNOSTIC,
|
||||||
entity_registry_enabled_default=False,
|
entity_registry_enabled_default=False,
|
||||||
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
native_unit_of_measurement=UnitOfTemperature.CELSIUS,
|
||||||
value_fn=lambda x: None if x.temperature is None else x.temperature / 1000,
|
value_fn=lambda x: _get_temperature(x.temperature),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
WAN_SENSOR_TYPES: tuple[SFRBoxSensorEntityDescription[WanInfo], ...] = (
|
WAN_SENSOR_TYPES: tuple[SFRBoxSensorEntityDescription[WanInfo], ...] = (
|
||||||
|
@ -203,23 +204,38 @@ WAN_SENSOR_TYPES: tuple[SFRBoxSensorEntityDescription[WanInfo], ...] = (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _value_to_option(value: str | None) -> str | None:
|
||||||
|
if value is None:
|
||||||
|
return value
|
||||||
|
return value.lower().replace(" ", "_").replace(".", "_")
|
||||||
|
|
||||||
|
|
||||||
|
def _get_temperature(value: float | None) -> float | None:
|
||||||
|
if value is None or value < 1000:
|
||||||
|
return value
|
||||||
|
return value / 1000
|
||||||
|
|
||||||
|
|
||||||
async def async_setup_entry(
|
async def async_setup_entry(
|
||||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set up the sensors."""
|
"""Set up the sensors."""
|
||||||
data: DomainData = hass.data[DOMAIN][entry.entry_id]
|
data: DomainData = hass.data[DOMAIN][entry.entry_id]
|
||||||
|
system_info = data.system.data
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
assert system_info is not None
|
||||||
|
|
||||||
entities: list[SFRBoxSensor] = [
|
entities: list[SFRBoxSensor] = [
|
||||||
SFRBoxSensor(data.system, description, data.system.data)
|
SFRBoxSensor(data.system, description, system_info)
|
||||||
for description in SYSTEM_SENSOR_TYPES
|
for description in SYSTEM_SENSOR_TYPES
|
||||||
]
|
]
|
||||||
entities.extend(
|
entities.extend(
|
||||||
SFRBoxSensor(data.wan, description, data.system.data)
|
SFRBoxSensor(data.wan, description, system_info)
|
||||||
for description in WAN_SENSOR_TYPES
|
for description in WAN_SENSOR_TYPES
|
||||||
)
|
)
|
||||||
if data.system.data.net_infra == "adsl":
|
if system_info.net_infra == "adsl":
|
||||||
entities.extend(
|
entities.extend(
|
||||||
SFRBoxSensor(data.dsl, description, data.system.data)
|
SFRBoxSensor(data.dsl, description, system_info)
|
||||||
for description in DSL_SENSOR_TYPES
|
for description in DSL_SENSOR_TYPES
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -251,4 +267,6 @@ class SFRBoxSensor[_T](CoordinatorEntity[SFRDataUpdateCoordinator[_T]], SensorEn
|
||||||
@property
|
@property
|
||||||
def native_value(self) -> StateType:
|
def native_value(self) -> StateType:
|
||||||
"""Return the native value of the device."""
|
"""Return the native value of the device."""
|
||||||
|
if self.coordinator.data is None:
|
||||||
|
return None
|
||||||
return self.entity_description.value_fn(self.coordinator.data)
|
return self.entity_description.value_fn(self.coordinator.data)
|
||||||
|
|
|
@ -2616,7 +2616,7 @@ sensoterra==2.0.1
|
||||||
sentry-sdk==1.40.3
|
sentry-sdk==1.40.3
|
||||||
|
|
||||||
# homeassistant.components.sfr_box
|
# homeassistant.components.sfr_box
|
||||||
sfrbox-api==0.0.8
|
sfrbox-api==0.0.10
|
||||||
|
|
||||||
# homeassistant.components.sharkiq
|
# homeassistant.components.sharkiq
|
||||||
sharkiq==1.0.2
|
sharkiq==1.0.2
|
||||||
|
|
|
@ -2074,7 +2074,7 @@ sensoterra==2.0.1
|
||||||
sentry-sdk==1.40.3
|
sentry-sdk==1.40.3
|
||||||
|
|
||||||
# homeassistant.components.sfr_box
|
# homeassistant.components.sfr_box
|
||||||
sfrbox-api==0.0.8
|
sfrbox-api==0.0.10
|
||||||
|
|
||||||
# homeassistant.components.sharkiq
|
# homeassistant.components.sharkiq
|
||||||
sharkiq==1.0.2
|
sharkiq==1.0.2
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
'product_id': 'NB6VAC-FXC-r0',
|
'product_id': 'NB6VAC-FXC-r0',
|
||||||
'refclient': '',
|
'refclient': '',
|
||||||
'serial_number': '**REDACTED**',
|
'serial_number': '**REDACTED**',
|
||||||
'temperature': 27560,
|
'temperature': 27560.0,
|
||||||
'uptime': 2353575,
|
'uptime': 2353575,
|
||||||
'version_bootloader': 'NB6VAC-BOOTLOADER-R4.0.8',
|
'version_bootloader': 'NB6VAC-BOOTLOADER-R4.0.8',
|
||||||
'version_dsldriver': 'NB6VAC-XDSL-A2pv6F039p',
|
'version_dsldriver': 'NB6VAC-XDSL-A2pv6F039p',
|
||||||
|
@ -90,7 +90,7 @@
|
||||||
'product_id': 'NB6VAC-FXC-r0',
|
'product_id': 'NB6VAC-FXC-r0',
|
||||||
'refclient': '',
|
'refclient': '',
|
||||||
'serial_number': '**REDACTED**',
|
'serial_number': '**REDACTED**',
|
||||||
'temperature': 27560,
|
'temperature': 27560.0,
|
||||||
'uptime': 2353575,
|
'uptime': 2353575,
|
||||||
'version_bootloader': 'NB6VAC-BOOTLOADER-R4.0.8',
|
'version_bootloader': 'NB6VAC-BOOTLOADER-R4.0.8',
|
||||||
'version_dsldriver': 'NB6VAC-XDSL-A2pv6F039p',
|
'version_dsldriver': 'NB6VAC-XDSL-A2pv6F039p',
|
||||||
|
|
Loading…
Reference in New Issue