Merge fritzbox_netmonitor integration into fritz (#52264)

* Merge netmonitor integration

* cleanup additional files

* Use attrs instead of properties

* Add state_class to relevant sensors

* Update homeassistant/components/fritz/sensor.py

Co-authored-by: Franck Nijhof <frenck@frenck.nl>

* Update homeassistant/components/fritz/sensor.py

Co-authored-by: Franck Nijhof <frenck@frenck.nl>

* Update homeassistant/components/fritz/sensor.py

Co-authored-by: Franck Nijhof <frenck@frenck.nl>

* Update homeassistant/components/fritz/sensor.py

Co-authored-by: Franck Nijhof <frenck@frenck.nl>

* Update homeassistant/components/fritz/sensor.py

Co-authored-by: Franck Nijhof <frenck@frenck.nl>

* Update homeassistant/components/fritz/sensor.py

Co-authored-by: Franck Nijhof <frenck@frenck.nl>

* mypy fix + small cleanup

* Round, GB and icons

* Update homeassistant/components/fritz/sensor.py

Co-authored-by: Franck Nijhof <frenck@frenck.nl>

* remove state_class from no-reset counters

Co-authored-by: Franck Nijhof <frenck@frenck.nl>
This commit is contained in:
Simone Chemelli 2021-07-03 15:26:43 +02:00 committed by GitHub
parent 4b7442412a
commit 69b9a9c4ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 84 additions and 184 deletions

View File

@ -348,7 +348,6 @@ omit =
homeassistant/components/fritzbox_callmonitor/const.py
homeassistant/components/fritzbox_callmonitor/base.py
homeassistant/components/fritzbox_callmonitor/sensor.py
homeassistant/components/fritzbox_netmonitor/sensor.py
homeassistant/components/fronius/sensor.py
homeassistant/components/frontier_silicon/media_player.py
homeassistant/components/futurenow/light.py

View File

@ -8,7 +8,7 @@ from typing import Callable, TypedDict
from fritzconnection.core.exceptions import FritzConnectionException
from fritzconnection.lib.fritzstatus import FritzStatus
from homeassistant.components.sensor import SensorEntity
from homeassistant.components.sensor import STATE_CLASS_MEASUREMENT, SensorEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import DEVICE_CLASS_TIMESTAMP
from homeassistant.core import HomeAssistant
@ -42,11 +42,43 @@ def _retrieve_external_ip_state(status: FritzStatus, last_value: str) -> str:
return status.external_ip
class SensorData(TypedDict):
def _retrieve_kib_s_sent_state(status: FritzStatus, last_value: str) -> str:
"""Return upload transmission rate."""
return round(status.transmission_rate[0] * 8 / 1024, 1)
def _retrieve_kib_s_received_state(status: FritzStatus, last_value: str) -> str:
"""Return download transmission rate."""
return round(status.transmission_rate[1] * 8 / 1024, 1)
def _retrieve_max_kib_s_sent_state(status: FritzStatus, last_value: str) -> str:
"""Return upload max transmission rate."""
return round(status.max_bit_rate[0] / 1024, 1)
def _retrieve_max_kib_s_received_state(status: FritzStatus, last_value: str) -> str:
"""Return download max transmission rate."""
return round(status.max_bit_rate[1] / 1024, 1)
def _retrieve_gb_sent_state(status: FritzStatus, last_value: str) -> str:
"""Return upload total data."""
return round(status.bytes_sent * 8 / 1024 / 1024 / 1024, 1)
def _retrieve_gb_received_state(status: FritzStatus, last_value: str) -> str:
"""Return download total data."""
return round(status.bytes_received * 8 / 1024 / 1024 / 1024, 1)
class SensorData(TypedDict, total=False):
"""Sensor data class."""
name: str
device_class: str | None
state_class: str | None
unit_of_measurement: str | None
icon: str | None
state_provider: Callable
@ -54,16 +86,52 @@ class SensorData(TypedDict):
SENSOR_DATA = {
"external_ip": SensorData(
name="External IP",
device_class=None,
icon="mdi:earth",
state_provider=_retrieve_external_ip_state,
),
"uptime": SensorData(
name="Uptime",
device_class=DEVICE_CLASS_TIMESTAMP,
icon=None,
state_provider=_retrieve_uptime_state,
),
"kib_s_sent": SensorData(
name="KiB/s sent",
state_class=STATE_CLASS_MEASUREMENT,
unit_of_measurement="KiB/s",
icon="mdi:upload",
state_provider=_retrieve_kib_s_sent_state,
),
"kib_s_received": SensorData(
name="KiB/s received",
state_class=STATE_CLASS_MEASUREMENT,
unit_of_measurement="KiB/s",
icon="mdi:download",
state_provider=_retrieve_kib_s_received_state,
),
"max_kib_s_sent": SensorData(
name="Max KiB/s sent",
unit_of_measurement="KiB/s",
icon="mdi:upload",
state_provider=_retrieve_max_kib_s_sent_state,
),
"max_kib_s_received": SensorData(
name="Max KiB/s received",
unit_of_measurement="KiB/s",
icon="mdi:download",
state_provider=_retrieve_max_kib_s_received_state,
),
"mb_sent": SensorData(
name="GB sent",
unit_of_measurement="GB",
icon="mdi:upload",
state_provider=_retrieve_gb_sent_state,
),
"mb_received": SensorData(
name="GB received",
unit_of_measurement="GB",
icon="mdi:download",
state_provider=_retrieve_gb_received_state,
),
}
@ -97,11 +165,14 @@ class FritzBoxSensor(FritzBoxBaseEntity, SensorEntity):
) -> None:
"""Init FRITZ!Box connectivity class."""
self._sensor_data: SensorData = SENSOR_DATA[sensor_type]
self._unique_id = f"{fritzbox_tools.unique_id}-{sensor_type}"
self._name = f"{device_friendly_name} {self._sensor_data['name']}"
self._is_available = True
self._last_value: str | None = None
self._state: str | None = None
self._attr_available = True
self._attr_device_class = self._sensor_data.get("device_class")
self._attr_icon = self._sensor_data.get("icon")
self._attr_name = f"{device_friendly_name} {self._sensor_data['name']}"
self._attr_state_class = self._sensor_data.get("state_class")
self._attr_unit_of_measurement = self._sensor_data.get("unit_of_measurement")
self._attr_unique_id = f"{fritzbox_tools.unique_id}-{sensor_type}"
super().__init__(fritzbox_tools, device_friendly_name)
@property
@ -109,46 +180,18 @@ class FritzBoxSensor(FritzBoxBaseEntity, SensorEntity):
"""Return the state provider for the binary sensor."""
return self._sensor_data["state_provider"]
@property
def name(self) -> str:
"""Return name."""
return self._name
@property
def device_class(self) -> str | None:
"""Return device class."""
return self._sensor_data["device_class"]
@property
def icon(self) -> str | None:
"""Return icon."""
return self._sensor_data["icon"]
@property
def unique_id(self) -> str:
"""Return unique id."""
return self._unique_id
@property
def state(self) -> str | None:
"""Return the state of the sensor."""
return self._state
@property
def available(self) -> bool:
"""Return availability."""
return self._is_available
def update(self) -> None:
"""Update data."""
_LOGGER.debug("Updating FRITZ!Box sensors")
try:
status: FritzStatus = self._fritzbox_tools.fritz_status
self._is_available = True
self._attr_available = True
except FritzConnectionException:
_LOGGER.error("Error getting the state from the FRITZ!Box", exc_info=True)
self._is_available = False
self._attr_available = False
return
self._state = self._last_value = self._state_provider(status, self._last_value)
self._attr_state = self._last_value = self._state_provider(
status, self._last_value
)

View File

@ -1 +0,0 @@
"""The fritzbox_netmonitor component."""

View File

@ -1,8 +0,0 @@
{
"domain": "fritzbox_netmonitor",
"name": "AVM FRITZ!Box Net Monitor",
"documentation": "https://www.home-assistant.io/integrations/fritzbox_netmonitor",
"requirements": ["fritzconnection==1.4.2"],
"codeowners": [],
"iot_class": "local_polling"
}

View File

@ -1,131 +0,0 @@
"""Support for monitoring an AVM Fritz!Box router."""
from datetime import timedelta
import logging
from fritzconnection.core.exceptions import FritzConnectionException
from fritzconnection.lib.fritzstatus import FritzStatus
from requests.exceptions import RequestException
import voluptuous as vol
from homeassistant.components.sensor import PLATFORM_SCHEMA, SensorEntity
from homeassistant.const import CONF_HOST, CONF_NAME, STATE_UNAVAILABLE
import homeassistant.helpers.config_validation as cv
from homeassistant.util import Throttle
_LOGGER = logging.getLogger(__name__)
DEFAULT_NAME = "fritz_netmonitor"
DEFAULT_HOST = "169.254.1.1" # This IP is valid for all FRITZ!Box routers.
ATTR_BYTES_RECEIVED = "bytes_received"
ATTR_BYTES_SENT = "bytes_sent"
ATTR_TRANSMISSION_RATE_UP = "transmission_rate_up"
ATTR_TRANSMISSION_RATE_DOWN = "transmission_rate_down"
ATTR_EXTERNAL_IP = "external_ip"
ATTR_IS_CONNECTED = "is_connected"
ATTR_IS_LINKED = "is_linked"
ATTR_MAX_BYTE_RATE_DOWN = "max_byte_rate_down"
ATTR_MAX_BYTE_RATE_UP = "max_byte_rate_up"
ATTR_UPTIME = "uptime"
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=5)
STATE_ONLINE = "online"
STATE_OFFLINE = "offline"
ICON = "mdi:web"
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
{
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string,
}
)
def setup_platform(hass, config, add_entities, discovery_info=None):
"""Set up the FRITZ!Box monitor sensors."""
name = config[CONF_NAME]
host = config[CONF_HOST]
try:
fstatus = FritzStatus(address=host)
except (ValueError, TypeError, FritzConnectionException):
fstatus = None
if fstatus is None:
_LOGGER.error("Failed to establish connection to FRITZ!Box: %s", host)
return 1
_LOGGER.info("Successfully connected to FRITZ!Box")
add_entities([FritzboxMonitorSensor(name, fstatus)], True)
class FritzboxMonitorSensor(SensorEntity):
"""Implementation of a fritzbox monitor sensor."""
def __init__(self, name, fstatus):
"""Initialize the sensor."""
self._name = name
self._fstatus = fstatus
self._state = STATE_UNAVAILABLE
self._is_linked = self._is_connected = None
self._external_ip = self._uptime = None
self._bytes_sent = self._bytes_received = None
self._transmission_rate_up = None
self._transmission_rate_down = None
self._max_byte_rate_up = self._max_byte_rate_down = None
@property
def name(self):
"""Return the name of the sensor."""
return self._name.rstrip()
@property
def icon(self):
"""Icon to use in the frontend, if any."""
return ICON
@property
def state(self):
"""Return the state of the device."""
return self._state
@property
def extra_state_attributes(self):
"""Return the device state attributes."""
# Don't return attributes if FritzBox is unreachable
if self._state == STATE_UNAVAILABLE:
return {}
return {
ATTR_IS_LINKED: self._is_linked,
ATTR_IS_CONNECTED: self._is_connected,
ATTR_EXTERNAL_IP: self._external_ip,
ATTR_UPTIME: self._uptime,
ATTR_BYTES_SENT: self._bytes_sent,
ATTR_BYTES_RECEIVED: self._bytes_received,
ATTR_TRANSMISSION_RATE_UP: self._transmission_rate_up,
ATTR_TRANSMISSION_RATE_DOWN: self._transmission_rate_down,
ATTR_MAX_BYTE_RATE_UP: self._max_byte_rate_up,
ATTR_MAX_BYTE_RATE_DOWN: self._max_byte_rate_down,
}
@Throttle(MIN_TIME_BETWEEN_UPDATES)
def update(self):
"""Retrieve information from the FritzBox."""
try:
self._is_linked = self._fstatus.is_linked
self._is_connected = self._fstatus.is_connected
self._external_ip = self._fstatus.external_ip
self._uptime = self._fstatus.uptime
self._bytes_sent = self._fstatus.bytes_sent
self._bytes_received = self._fstatus.bytes_received
transmission_rate = self._fstatus.transmission_rate
self._transmission_rate_up = transmission_rate[0]
self._transmission_rate_down = transmission_rate[1]
self._max_byte_rate_up = self._fstatus.max_byte_rate[0]
self._max_byte_rate_down = self._fstatus.max_byte_rate[1]
self._state = STATE_ONLINE if self._is_connected else STATE_OFFLINE
except RequestException as err:
self._state = STATE_UNAVAILABLE
_LOGGER.warning("Could not reach FRITZ!Box: %s", err)

View File

@ -637,7 +637,6 @@ freesms==0.2.0
# homeassistant.components.fritz
# homeassistant.components.fritzbox_callmonitor
# homeassistant.components.fritzbox_netmonitor
fritzconnection==1.4.2
# homeassistant.components.fritz

View File

@ -346,7 +346,6 @@ freebox-api==0.0.10
# homeassistant.components.fritz
# homeassistant.components.fritzbox_callmonitor
# homeassistant.components.fritzbox_netmonitor
fritzconnection==1.4.2
# homeassistant.components.fritz