From 69b9a9c4ee9096274c72066dc55e3204b445866b Mon Sep 17 00:00:00 2001 From: Simone Chemelli Date: Sat, 3 Jul 2021 15:26:43 +0200 Subject: [PATCH] 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 * Update homeassistant/components/fritz/sensor.py Co-authored-by: Franck Nijhof * Update homeassistant/components/fritz/sensor.py Co-authored-by: Franck Nijhof * Update homeassistant/components/fritz/sensor.py Co-authored-by: Franck Nijhof * Update homeassistant/components/fritz/sensor.py Co-authored-by: Franck Nijhof * Update homeassistant/components/fritz/sensor.py Co-authored-by: Franck Nijhof * mypy fix + small cleanup * Round, GB and icons * Update homeassistant/components/fritz/sensor.py Co-authored-by: Franck Nijhof * remove state_class from no-reset counters Co-authored-by: Franck Nijhof --- .coveragerc | 1 - homeassistant/components/fritz/sensor.py | 125 +++++++++++------ .../fritzbox_netmonitor/__init__.py | 1 - .../fritzbox_netmonitor/manifest.json | 8 -- .../components/fritzbox_netmonitor/sensor.py | 131 ------------------ requirements_all.txt | 1 - requirements_test_all.txt | 1 - 7 files changed, 84 insertions(+), 184 deletions(-) delete mode 100644 homeassistant/components/fritzbox_netmonitor/__init__.py delete mode 100644 homeassistant/components/fritzbox_netmonitor/manifest.json delete mode 100644 homeassistant/components/fritzbox_netmonitor/sensor.py diff --git a/.coveragerc b/.coveragerc index cbdcc60b4211..6bf9936e26b6 100644 --- a/.coveragerc +++ b/.coveragerc @@ -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 diff --git a/homeassistant/components/fritz/sensor.py b/homeassistant/components/fritz/sensor.py index 6d3a8f33c3c9..042ec069864e 100644 --- a/homeassistant/components/fritz/sensor.py +++ b/homeassistant/components/fritz/sensor.py @@ -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 + ) diff --git a/homeassistant/components/fritzbox_netmonitor/__init__.py b/homeassistant/components/fritzbox_netmonitor/__init__.py deleted file mode 100644 index 8bea1da4a44b..000000000000 --- a/homeassistant/components/fritzbox_netmonitor/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""The fritzbox_netmonitor component.""" diff --git a/homeassistant/components/fritzbox_netmonitor/manifest.json b/homeassistant/components/fritzbox_netmonitor/manifest.json deleted file mode 100644 index b52872fc0448..000000000000 --- a/homeassistant/components/fritzbox_netmonitor/manifest.json +++ /dev/null @@ -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" -} diff --git a/homeassistant/components/fritzbox_netmonitor/sensor.py b/homeassistant/components/fritzbox_netmonitor/sensor.py deleted file mode 100644 index 3c37de7664c6..000000000000 --- a/homeassistant/components/fritzbox_netmonitor/sensor.py +++ /dev/null @@ -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) diff --git a/requirements_all.txt b/requirements_all.txt index 714b7495a188..c4be3f3a711a 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -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 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 51483cff9f35..b06ef05ef648 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -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