1 """Represent the Freebox router and its devices and sensors."""
3 from __future__
import annotations
5 from collections.abc
import Callable, Mapping
6 from contextlib
import suppress
7 from datetime
import datetime
11 from pathlib
import Path
13 from typing
import Any
15 from freebox_api
import Freepybox
16 from freebox_api.api.call
import Call
17 from freebox_api.api.home
import Home
18 from freebox_api.api.wifi
import Wifi
19 from freebox_api.exceptions
import HttpRequestError, NotOpenError
32 CONNECTION_SENSORS_KEYS,
34 HOME_COMPATIBLE_CATEGORIES,
39 _LOGGER = logging.getLogger(__name__)
43 """Validate if a String is a JSON value or not."""
46 except (ValueError, TypeError)
as err:
48 "Failed to parse JSON '%s', error '%s'",
56 async
def get_api(hass: HomeAssistant, host: str) -> Freepybox:
57 """Get the Freebox API."""
58 freebox_path =
Store(hass, STORAGE_VERSION, STORAGE_KEY).path
60 if not os.path.exists(freebox_path):
61 await hass.async_add_executor_job(os.makedirs, freebox_path)
63 token_file = Path(f
"{freebox_path}/{slugify(host)}.conf")
65 return Freepybox(APP_DESC, token_file, API_VERSION)
70 ) -> tuple[bool, list[dict[str, Any]]]:
71 """Hosts list is not supported when freebox is configured in bridge mode."""
72 supports_hosts: bool =
True
73 fbx_devices: list[dict[str, Any]] = []
75 fbx_devices = await fbx_api.lan.get_hosts_list()
or []
76 except HttpRequestError
as err:
78 (matcher := re.search(
r"Request failed \(APIResponse: (.+)\)",
str(err)))
79 and is_json(json_str := matcher.group(1))
80 and (json_resp := json.loads(json_str)).
get(
"error_code") ==
"nodev"
83 supports_hosts =
False
85 "Host list is not available using bridge mode (%s)",
92 return supports_hosts, fbx_devices
96 """Representation of a Freebox router."""
103 freebox_config: Mapping[str, Any],
105 """Initialize a Freebox router."""
107 self.
_host_host = entry.data[CONF_HOST]
108 self.
_port_port = entry.data[CONF_PORT]
110 self._api: Freepybox = api
111 self.name: str = freebox_config[
"model_info"][
"pretty_name"]
112 self.mac: str = freebox_config[
"mac"]
113 self._sw_v: str = freebox_config[
"firmware_version"]
114 self.
_attrs_attrs: dict[str, Any] = {}
117 self.devices: dict[str, dict[str, Any]] = {}
118 self.disks: dict[int, dict[str, Any]] = {}
120 self.raids: dict[int, dict[str, Any]] = {}
121 self.sensors_temperature: dict[str, int] = {}
122 self.sensors_connection: dict[str, float] = {}
123 self.
call_listcall_list: list[dict[str, Any]] = []
125 self.home_devices: dict[str, Any] = {}
126 self.listeners: list[Callable[[],
None]] = []
128 async
def update_all(self, now: datetime |
None =
None) ->
None:
129 """Update all Freebox platforms."""
135 """Update Freebox devices."""
138 fbx_devices: list[dict[str, Any]] = []
149 "primary_name": self.name,
150 "l2ident": {
"id": self.mac},
151 "vendor_name":
"Freebox SAS",
152 "host_type":
"router",
154 "attrs": self.
_attrs_attrs,
158 for fbx_device
in fbx_devices:
159 device_mac = fbx_device[
"l2ident"][
"id"]
161 if self.devices.
get(device_mac)
is None:
164 self.devices[device_mac] = fbx_device
172 """Update Freebox sensors."""
175 syst_datas: dict[str, Any] = await self._api.system.get_config()
179 for sensor
in syst_datas[
"sensors"]:
180 self.sensors_temperature[sensor[
"name"]] = sensor.get(
"value")
183 connection_datas: dict[str, Any] = await self._api.connection.get_status()
184 for sensor_key
in CONNECTION_SENSORS_KEYS:
185 self.sensors_connection[sensor_key] = connection_datas[sensor_key]
188 "IPv4": connection_datas.get(
"ipv4"),
189 "IPv6": connection_datas.get(
"ipv6"),
190 "connection_type": connection_datas[
"media"],
191 "uptime": datetime.fromtimestamp(
192 round(datetime.now().timestamp()) - syst_datas[
"uptime_val"]
194 "firmware_version": self._sw_v,
195 "serial": syst_datas[
"serial"],
198 self.
call_listcall_list = await self._api.call.get_calls_log()
206 """Update Freebox disks."""
208 fbx_disks: list[dict[str, Any]] = await self._api.storage.get_disks()
or []
210 for fbx_disk
in fbx_disks:
211 disk: dict[str, Any] = {**fbx_disk}
212 disk_part: dict[int, dict[str, Any]] = {}
213 for fbx_disk_part
in fbx_disk[
"partitions"]:
214 disk_part[fbx_disk_part[
"id"]] = fbx_disk_part
215 disk[
"partitions"] = disk_part
216 self.disks[fbx_disk[
"id"]] = disk
219 """Update Freebox raids."""
225 fbx_raids: list[dict[str, Any]] = await self._api.storage.get_raids()
or []
226 except HttpRequestError:
229 "Router %s API does not support RAID",
234 for fbx_raid
in fbx_raids:
235 self.raids[fbx_raid[
"id"]] = fbx_raid
238 """Update Home devices (alarm, light, sensor, switch, remote ...)."""
243 home_nodes: list[Any] = await self.
homehome.get_home_nodes()
or []
244 except HttpRequestError:
246 _LOGGER.warning(
"Home access is not granted")
250 for home_node
in home_nodes:
251 if home_node[
"category"]
in HOME_COMPATIBLE_CATEGORIES:
252 if self.home_devices.
get(home_node[
"id"])
is None:
254 self.home_devices[home_node[
"id"]] = home_node
262 """Reboot the Freebox."""
263 await self._api.system.reboot()
266 """Close the connection."""
267 with suppress(NotOpenError):
268 await self._api.
close()
272 """Return the device information."""
274 configuration_url=f
"https://{self._host}:{self._port}/",
275 connections={(CONNECTION_NETWORK_MAC, self.mac)},
276 identifiers={(DOMAIN, self.mac)},
277 manufacturer=
"Freebox SAS",
279 sw_version=self._sw_v,
284 """Event specific per Freebox entry to signal new device."""
285 return f
"{DOMAIN}-{self._host}-device-new"
289 """Event specific per Freebox entry to signal new home device."""
290 return f
"{DOMAIN}-{self._host}-home-device-new"
294 """Event specific per Freebox entry to signal updates in devices."""
295 return f
"{DOMAIN}-{self._host}-device-update"
299 """Event specific per Freebox entry to signal updates in sensors."""
300 return f
"{DOMAIN}-{self._host}-sensor-update"
304 """Event specific per Freebox entry to signal update in home devices."""
305 return f
"{DOMAIN}-{self._host}-home-device-update"
309 """Return sensors."""
310 return {**self.sensors_temperature, **self.sensors_connection}
314 """Return the call."""
315 return self._api.call
319 """Return the wifi."""
320 return self._api.wifi
324 """Return the home."""
325 return self._api.home
str signal_home_device_new(self)
DeviceInfo device_info(self)
dict[str, Any] sensors(self)
None update_home_devices(self)
str signal_device_update(self)
None update_all(self, datetime|None now=None)
str signal_device_new(self)
None update_sensors(self)
str signal_home_device_update(self)
None __init__(self, HomeAssistant hass, ConfigEntry entry, Freepybox api, Mapping[str, Any] freebox_config)
None update_device_trackers(self)
str signal_sensor_update(self)
None _update_disks_sensors(self)
None _update_raids_sensors(self)
web.Response get(self, web.Request request, str config_key)
Freepybox get_api(HomeAssistant hass, str host)
tuple[bool, list[dict[str, Any]]] get_hosts_list_if_supported(Freepybox fbx_api)
bool is_json(str json_str)
None async_dispatcher_send(HomeAssistant hass, str signal, *Any args)