mirror of
https://github.com/home-assistant/core.git
synced 2025-04-24 17:27:52 +00:00
Create Fritz device and connectivity sensor (#49699)
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
0df9454310
commit
55c96ae86f
@ -330,6 +330,7 @@ omit =
|
||||
homeassistant/components/freebox/sensor.py
|
||||
homeassistant/components/freebox/switch.py
|
||||
homeassistant/components/fritz/__init__.py
|
||||
homeassistant/components/fritz/binary_sensor.py
|
||||
homeassistant/components/fritz/common.py
|
||||
homeassistant/components/fritz/const.py
|
||||
homeassistant/components/fritz/device_tracker.py
|
||||
|
@ -13,7 +13,6 @@ from homeassistant.const import (
|
||||
)
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .common import FritzBoxTools, FritzData
|
||||
from .const import DATA_FRITZ, DOMAIN, PLATFORMS
|
||||
@ -59,7 +58,7 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigType) -> bool:
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload FRITZ!Box Tools config entry."""
|
||||
fritzbox: FritzBoxTools = hass.data[DOMAIN][entry.entry_id]
|
||||
fritzbox.async_unload()
|
||||
|
87
homeassistant/components/fritz/binary_sensor.py
Normal file
87
homeassistant/components/fritz/binary_sensor.py
Normal file
@ -0,0 +1,87 @@
|
||||
"""AVM FRITZ!Box connectivitiy sensor."""
|
||||
import logging
|
||||
|
||||
from fritzconnection.core.exceptions import FritzConnectionException
|
||||
|
||||
from homeassistant.components.binary_sensor import (
|
||||
DEVICE_CLASS_CONNECTIVITY,
|
||||
BinarySensorEntity,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .common import FritzBoxBaseEntity, FritzBoxTools
|
||||
from .const import DOMAIN
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities
|
||||
) -> None:
|
||||
"""Set up entry."""
|
||||
_LOGGER.debug("Setting up FRITZ!Box binary sensors")
|
||||
fritzbox_tools = hass.data[DOMAIN][entry.entry_id]
|
||||
|
||||
if "WANIPConn1" in fritzbox_tools.connection.services:
|
||||
# Only routers are supported at the moment
|
||||
async_add_entities(
|
||||
[FritzBoxConnectivitySensor(fritzbox_tools, entry.title)], True
|
||||
)
|
||||
|
||||
|
||||
class FritzBoxConnectivitySensor(FritzBoxBaseEntity, BinarySensorEntity):
|
||||
"""Define FRITZ!Box connectivity class."""
|
||||
|
||||
def __init__(self, fritzbox_tools: FritzBoxTools, device_friendlyname: str) -> None:
|
||||
"""Init FRITZ!Box connectivity class."""
|
||||
self._unique_id = f"{fritzbox_tools.unique_id}-connectivity"
|
||||
self._name = f"{device_friendlyname} Connectivity"
|
||||
self._is_on = True
|
||||
self._is_available = True
|
||||
super().__init__(fritzbox_tools, device_friendlyname)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return name."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def device_class(self):
|
||||
"""Return device class."""
|
||||
return DEVICE_CLASS_CONNECTIVITY
|
||||
|
||||
@property
|
||||
def is_on(self) -> bool:
|
||||
"""Return status."""
|
||||
return self._is_on
|
||||
|
||||
@property
|
||||
def unique_id(self):
|
||||
"""Return unique id."""
|
||||
return self._unique_id
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return availability."""
|
||||
return self._is_available
|
||||
|
||||
def update(self) -> None:
|
||||
"""Update data."""
|
||||
_LOGGER.debug("Updating FRITZ!Box binary sensors")
|
||||
self._is_on = True
|
||||
try:
|
||||
if "WANCommonInterfaceConfig1" in self._fritzbox_tools.connection.services:
|
||||
link_props = self._fritzbox_tools.connection.call_action(
|
||||
"WANCommonInterfaceConfig1", "GetCommonLinkProperties"
|
||||
)
|
||||
is_up = link_props["NewPhysicalLinkStatus"]
|
||||
self._is_on = is_up == "Up"
|
||||
else:
|
||||
self._is_on = self._fritzbox_tools.fritzstatus.is_connected
|
||||
|
||||
self._is_available = True
|
||||
|
||||
except FritzConnectionException:
|
||||
_LOGGER.error("Error getting the state from the FRITZ!Box", exc_info=True)
|
||||
self._is_available = False
|
@ -12,6 +12,7 @@ from fritzconnection.lib.fritzhosts import FritzHosts
|
||||
from fritzconnection.lib.fritzstatus import FritzStatus
|
||||
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_send
|
||||
from homeassistant.helpers.event import async_track_time_interval
|
||||
from homeassistant.util import dt as dt_util
|
||||
@ -49,7 +50,6 @@ class FritzBoxTools:
|
||||
):
|
||||
"""Initialize FritzboxTools class."""
|
||||
self._cancel_scan = None
|
||||
self._device_info = None
|
||||
self._devices: dict[str, Any] = {}
|
||||
self._unique_id = None
|
||||
self.connection = None
|
||||
@ -60,6 +60,9 @@ class FritzBoxTools:
|
||||
self.password = password
|
||||
self.port = port
|
||||
self.username = username
|
||||
self.mac = None
|
||||
self.model = None
|
||||
self.sw_version = None
|
||||
|
||||
async def async_setup(self):
|
||||
"""Wrap up FritzboxTools class setup."""
|
||||
@ -76,12 +79,13 @@ class FritzBoxTools:
|
||||
)
|
||||
|
||||
self.fritzstatus = FritzStatus(fc=self.connection)
|
||||
info = self.connection.call_action("DeviceInfo:1", "GetInfo")
|
||||
if self._unique_id is None:
|
||||
self._unique_id = self.connection.call_action("DeviceInfo:1", "GetInfo")[
|
||||
"NewSerialNumber"
|
||||
]
|
||||
self._unique_id = info["NewSerialNumber"]
|
||||
|
||||
self._device_info = self._fetch_device_info()
|
||||
self.model = info.get("NewModelName")
|
||||
self.sw_version = info.get("NewSoftwareVersion")
|
||||
self.mac = self.unique_id
|
||||
|
||||
async def async_start(self):
|
||||
"""Start FritzHosts connection."""
|
||||
@ -106,16 +110,6 @@ class FritzBoxTools:
|
||||
"""Return unique id."""
|
||||
return self._unique_id
|
||||
|
||||
@property
|
||||
def fritzbox_model(self):
|
||||
"""Return model."""
|
||||
return self._device_info["model"].replace("FRITZ!Box ", "")
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
"""Return device info."""
|
||||
return self._device_info
|
||||
|
||||
@property
|
||||
def devices(self) -> dict[str, Any]:
|
||||
"""Return devices."""
|
||||
@ -163,33 +157,13 @@ class FritzBoxTools:
|
||||
if new_device:
|
||||
async_dispatcher_send(self.hass, self.signal_device_new)
|
||||
|
||||
def _fetch_device_info(self):
|
||||
"""Fetch device info."""
|
||||
info = self.connection.call_action("DeviceInfo:1", "GetInfo")
|
||||
|
||||
dev_info = {}
|
||||
dev_info["identifiers"] = {
|
||||
# Serial numbers are unique identifiers within a specific domain
|
||||
(DOMAIN, self.unique_id)
|
||||
}
|
||||
dev_info["manufacturer"] = "AVM"
|
||||
|
||||
if dev_name := info.get("NewName"):
|
||||
dev_info["name"] = dev_name
|
||||
if dev_model := info.get("NewModelName"):
|
||||
dev_info["model"] = dev_model
|
||||
if dev_sw_ver := info.get("NewSoftwareVersion"):
|
||||
dev_info["sw_version"] = dev_sw_ver
|
||||
|
||||
return dev_info
|
||||
|
||||
|
||||
class FritzData:
|
||||
"""Storage class for platform global data."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize the data."""
|
||||
self.tracked = {}
|
||||
self.tracked: dict = {}
|
||||
|
||||
|
||||
class FritzDevice:
|
||||
@ -241,3 +215,30 @@ class FritzDevice:
|
||||
def last_activity(self):
|
||||
"""Return device last activity."""
|
||||
return self._last_activity
|
||||
|
||||
|
||||
class FritzBoxBaseEntity:
|
||||
"""Fritz host entity base class."""
|
||||
|
||||
def __init__(self, fritzbox_tools: FritzBoxTools, device_name: str) -> None:
|
||||
"""Init device info class."""
|
||||
self._fritzbox_tools = fritzbox_tools
|
||||
self._device_name = device_name
|
||||
|
||||
@property
|
||||
def mac_address(self) -> str:
|
||||
"""Return the mac address of the main device."""
|
||||
return self._fritzbox_tools.mac
|
||||
|
||||
@property
|
||||
def device_info(self):
|
||||
"""Return the device information."""
|
||||
|
||||
return {
|
||||
"connections": {(CONNECTION_NETWORK_MAC, self.mac_address)},
|
||||
"identifiers": {(DOMAIN, self._fritzbox_tools.unique_id)},
|
||||
"name": self._device_name,
|
||||
"manufacturer": "AVM",
|
||||
"model": self._fritzbox_tools.model,
|
||||
"sw_version": self._fritzbox_tools.sw_version,
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
"""Config flow to configure the FRITZ!Box Tools integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from urllib.parse import urlparse
|
||||
|
||||
@ -65,7 +67,7 @@ class FritzBoxToolsFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
|
||||
return None
|
||||
|
||||
async def async_check_configured_entry(self) -> ConfigEntry:
|
||||
async def async_check_configured_entry(self) -> ConfigEntry | None:
|
||||
"""Check if entry is configured."""
|
||||
for entry in self._async_current_entries(include_ignore=False):
|
||||
if entry.data[CONF_HOST] == self._host:
|
||||
@ -170,7 +172,7 @@ class FritzBoxToolsFlowHandler(ConfigFlow, domain=DOMAIN):
|
||||
self._password = user_input[CONF_PASSWORD]
|
||||
|
||||
if not (error := await self.fritz_tools_init()):
|
||||
self._name = self.fritz_tools.device_info["model"]
|
||||
self._name = self.fritz_tools.model
|
||||
|
||||
if await self.async_check_configured_entry():
|
||||
error = "already_configured"
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
DOMAIN = "fritz"
|
||||
|
||||
PLATFORMS = ["device_tracker"]
|
||||
PLATFORMS = ["binary_sensor", "device_tracker"]
|
||||
|
||||
DATA_FRITZ = "fritz_data"
|
||||
|
||||
|
@ -17,7 +17,6 @@ from homeassistant.core import HomeAssistant, callback
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from .common import FritzBoxTools
|
||||
@ -116,7 +115,7 @@ class FritzBoxTracker(ScannerEntity):
|
||||
self._mac = device.mac_address
|
||||
self._name = device.hostname or DEFAULT_DEVICE_NAME
|
||||
self._active = False
|
||||
self._attrs = {}
|
||||
self._attrs: dict = {}
|
||||
|
||||
@property
|
||||
def is_connected(self):
|
||||
@ -154,7 +153,7 @@ class FritzBoxTracker(ScannerEntity):
|
||||
return SOURCE_TYPE_ROUTER
|
||||
|
||||
@property
|
||||
def device_info(self) -> DeviceInfo:
|
||||
def device_info(self):
|
||||
"""Return the device information."""
|
||||
return {
|
||||
"connections": {(CONNECTION_NETWORK_MAC, self._mac)},
|
||||
@ -162,6 +161,10 @@ class FritzBoxTracker(ScannerEntity):
|
||||
"name": self.name,
|
||||
"manufacturer": "AVM",
|
||||
"model": "FRITZ!Box Tracked device",
|
||||
"via_device": (
|
||||
DOMAIN,
|
||||
self._router.unique_id,
|
||||
),
|
||||
}
|
||||
|
||||
@property
|
||||
|
@ -49,7 +49,7 @@ class FritzConnectionMock: # pylint: disable=too-few-public-methods
|
||||
"NewBytesReceived": 12045,
|
||||
},
|
||||
("DeviceInfo:1", "GetInfo"): {
|
||||
"NewSerialNumber": 1234,
|
||||
"NewSerialNumber": "abcdefgh",
|
||||
"NewName": "TheName",
|
||||
"NewModelName": "FRITZ!Box 7490",
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user