Add temperature sensors for Asuswrt (#58303)
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>pull/60936/head
parent
0dfc86956b
commit
ac26c2378b
|
@ -25,3 +25,4 @@ SENSORS_BYTES = ["sensor_rx_bytes", "sensor_tx_bytes"]
|
|||
SENSORS_CONNECTED_DEVICE = ["sensor_connected_device"]
|
||||
SENSORS_LOAD_AVG = ["sensor_load_avg1", "sensor_load_avg5", "sensor_load_avg15"]
|
||||
SENSORS_RATES = ["sensor_rx_rates", "sensor_tx_rates"]
|
||||
SENSORS_TEMPERATURES = ["2.4GHz", "5.0GHz", "CPU"]
|
||||
|
|
|
@ -47,6 +47,7 @@ from .const import (
|
|||
SENSORS_CONNECTED_DEVICE,
|
||||
SENSORS_LOAD_AVG,
|
||||
SENSORS_RATES,
|
||||
SENSORS_TEMPERATURES,
|
||||
)
|
||||
|
||||
CONF_REQ_RELOAD = [CONF_DNSMASQ, CONF_INTERFACE, CONF_REQUIRE_IP]
|
||||
|
@ -60,6 +61,7 @@ SENSORS_TYPE_BYTES = "sensors_bytes"
|
|||
SENSORS_TYPE_COUNT = "sensors_count"
|
||||
SENSORS_TYPE_LOAD_AVG = "sensors_load_avg"
|
||||
SENSORS_TYPE_RATES = "sensors_rates"
|
||||
SENSORS_TYPE_TEMPERATURES = "sensors_temperatures"
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -114,6 +116,15 @@ class AsusWrtSensorDataHandler:
|
|||
|
||||
return _get_dict(SENSORS_LOAD_AVG, avg)
|
||||
|
||||
async def _get_temperatures(self):
|
||||
"""Fetch temperatures information from the router."""
|
||||
try:
|
||||
temperatures = await self._api.async_get_temperature()
|
||||
except (OSError, ValueError) as exc:
|
||||
raise UpdateFailed(exc) from exc
|
||||
|
||||
return temperatures
|
||||
|
||||
def update_device_count(self, conn_devices: int):
|
||||
"""Update connected devices attribute."""
|
||||
if self._connected_devices == conn_devices:
|
||||
|
@ -131,6 +142,8 @@ class AsusWrtSensorDataHandler:
|
|||
method = self._get_load_avg
|
||||
elif sensor_type == SENSORS_TYPE_RATES:
|
||||
method = self._get_rates
|
||||
elif sensor_type == SENSORS_TYPE_TEMPERATURES:
|
||||
method = self._get_temperatures
|
||||
else:
|
||||
raise RuntimeError(f"Invalid sensor type: {sensor_type}")
|
||||
|
||||
|
@ -349,9 +362,14 @@ class AsusWrtRouter:
|
|||
SENSORS_TYPE_COUNT: SENSORS_CONNECTED_DEVICE,
|
||||
SENSORS_TYPE_LOAD_AVG: SENSORS_LOAD_AVG,
|
||||
SENSORS_TYPE_RATES: SENSORS_RATES,
|
||||
SENSORS_TYPE_TEMPERATURES: SENSORS_TEMPERATURES,
|
||||
}
|
||||
|
||||
for sensor_type, sensor_names in sensors_types.items():
|
||||
if sensor_type == SENSORS_TYPE_TEMPERATURES:
|
||||
sensor_names = await self._get_available_temperature_sensors()
|
||||
if not sensor_names:
|
||||
continue
|
||||
coordinator = await self._sensors_data_handler.get_coordinator(
|
||||
sensor_type, sensor_type != SENSORS_TYPE_COUNT
|
||||
)
|
||||
|
@ -370,6 +388,23 @@ class AsusWrtRouter:
|
|||
if self._sensors_data_handler.update_device_count(self._connected_devices):
|
||||
await coordinator.async_refresh()
|
||||
|
||||
async def _get_available_temperature_sensors(self):
|
||||
"""Check which temperature information is available on the router."""
|
||||
try:
|
||||
availability = await self._api.async_find_temperature_commands()
|
||||
available_sensors = [
|
||||
SENSORS_TEMPERATURES[i] for i in range(3) if availability[i]
|
||||
]
|
||||
except Exception as exc: # pylint: disable=broad-except
|
||||
_LOGGER.debug(
|
||||
"Failed checking temperature sensor availability for ASUS router %s. Exception: %s",
|
||||
self._host,
|
||||
exc,
|
||||
)
|
||||
return []
|
||||
|
||||
return available_sensors
|
||||
|
||||
async def close(self) -> None:
|
||||
"""Close the connection."""
|
||||
if self._api is not None and self._protocol == PROTOCOL_TELNET:
|
||||
|
|
|
@ -5,12 +5,17 @@ from dataclasses import dataclass
|
|||
from numbers import Real
|
||||
|
||||
from homeassistant.components.sensor import (
|
||||
SensorDeviceClass,
|
||||
SensorEntity,
|
||||
SensorEntityDescription,
|
||||
SensorStateClass,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import DATA_GIGABYTES, DATA_RATE_MEGABITS_PER_SECOND
|
||||
from homeassistant.const import (
|
||||
DATA_GIGABYTES,
|
||||
DATA_RATE_MEGABITS_PER_SECOND,
|
||||
TEMP_CELSIUS,
|
||||
)
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers.entity import EntityCategory
|
||||
from homeassistant.helpers.update_coordinator import (
|
||||
|
@ -25,6 +30,7 @@ from .const import (
|
|||
SENSORS_CONNECTED_DEVICE,
|
||||
SENSORS_LOAD_AVG,
|
||||
SENSORS_RATES,
|
||||
SENSORS_TEMPERATURES,
|
||||
)
|
||||
from .router import KEY_COORDINATOR, KEY_SENSORS, AsusWrtRouter
|
||||
|
||||
|
@ -114,6 +120,39 @@ CONNECTION_SENSORS: tuple[AsusWrtSensorEntityDescription, ...] = (
|
|||
factor=1,
|
||||
precision=1,
|
||||
),
|
||||
AsusWrtSensorEntityDescription(
|
||||
key=SENSORS_TEMPERATURES[0],
|
||||
name="2.4GHz Temperature",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
native_unit_of_measurement=TEMP_CELSIUS,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
factor=1,
|
||||
precision=1,
|
||||
),
|
||||
AsusWrtSensorEntityDescription(
|
||||
key=SENSORS_TEMPERATURES[1],
|
||||
name="5GHz Temperature",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
native_unit_of_measurement=TEMP_CELSIUS,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
factor=1,
|
||||
precision=1,
|
||||
),
|
||||
AsusWrtSensorEntityDescription(
|
||||
key=SENSORS_TEMPERATURES[2],
|
||||
name="CPU Temperature",
|
||||
state_class=SensorStateClass.MEASUREMENT,
|
||||
device_class=SensorDeviceClass.TEMPERATURE,
|
||||
native_unit_of_measurement=TEMP_CELSIUS,
|
||||
entity_category=EntityCategory.DIAGNOSTIC,
|
||||
entity_registry_enabled_default=False,
|
||||
factor=1,
|
||||
precision=1,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ CONFIG_DATA = {
|
|||
MOCK_BYTES_TOTAL = [60000000000, 50000000000]
|
||||
MOCK_CURRENT_TRANSFER_RATES = [20000000, 10000000]
|
||||
MOCK_LOAD_AVG = [1.1, 1.2, 1.3]
|
||||
MOCK_TEMPERATURES = {"2.4GHz": 40, "5.0GHz": 0, "CPU": 71.2}
|
||||
|
||||
SENSOR_NAMES = [
|
||||
"Devices Connected",
|
||||
|
@ -50,6 +51,9 @@ SENSOR_NAMES = [
|
|||
"Load Avg (1m)",
|
||||
"Load Avg (5m)",
|
||||
"Load Avg (15m)",
|
||||
"2.4GHz Temperature",
|
||||
"5GHz Temperature",
|
||||
"CPU Temperature",
|
||||
]
|
||||
|
||||
|
||||
|
@ -62,8 +66,16 @@ def mock_devices_fixture():
|
|||
}
|
||||
|
||||
|
||||
@pytest.fixture(name="mock_available_temps")
|
||||
def mock_available_temps_list():
|
||||
"""Mock a list of available temperature sensors."""
|
||||
|
||||
# Only length of 3 booleans is valid. First checking the exception handling.
|
||||
return [True, False]
|
||||
|
||||
|
||||
@pytest.fixture(name="connect")
|
||||
def mock_controller_connect(mock_devices):
|
||||
def mock_controller_connect(mock_devices, mock_available_temps):
|
||||
"""Mock a successful connection."""
|
||||
with patch("homeassistant.components.asuswrt.router.AsusWrt") as service_mock:
|
||||
service_mock.return_value.connection.async_connect = AsyncMock()
|
||||
|
@ -88,10 +100,16 @@ def mock_controller_connect(mock_devices):
|
|||
service_mock.return_value.async_get_loadavg = AsyncMock(
|
||||
return_value=MOCK_LOAD_AVG
|
||||
)
|
||||
service_mock.return_value.async_get_temperature = AsyncMock(
|
||||
return_value=MOCK_TEMPERATURES
|
||||
)
|
||||
service_mock.return_value.async_find_temperature_commands = AsyncMock(
|
||||
return_value=mock_available_temps
|
||||
)
|
||||
yield service_mock
|
||||
|
||||
|
||||
async def test_sensors(hass, connect, mock_devices):
|
||||
async def test_sensors(hass, connect, mock_devices, mock_available_temps):
|
||||
"""Test creating an AsusWRT sensor."""
|
||||
entity_reg = er.async_get(hass)
|
||||
|
||||
|
@ -137,6 +155,11 @@ async def test_sensors(hass, connect, mock_devices):
|
|||
assert hass.states.get(f"{sensor_prefix}_load_avg_15m").state == "1.3"
|
||||
assert hass.states.get(f"{sensor_prefix}_devices_connected").state == "2"
|
||||
|
||||
# assert temperature availability exception is handled correctly
|
||||
assert not hass.states.get(f"{sensor_prefix}_2_4ghz_temperature")
|
||||
assert not hass.states.get(f"{sensor_prefix}_5ghz_temperature")
|
||||
assert not hass.states.get(f"{sensor_prefix}_cpu_temperature")
|
||||
|
||||
# add one device and remove another
|
||||
mock_devices.pop("a1:b1:c1:d1:e1:f1")
|
||||
mock_devices["a3:b3:c3:d3:e3:f3"] = Device(
|
||||
|
@ -161,3 +184,15 @@ async def test_sensors(hass, connect, mock_devices):
|
|||
|
||||
# consider home option not set, device "test" not home
|
||||
assert hass.states.get(f"{device_tracker.DOMAIN}.test").state == STATE_NOT_HOME
|
||||
|
||||
# checking temperature sensors without exceptions
|
||||
mock_available_temps.append(True)
|
||||
await hass.config_entries.async_reload(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
async_fire_time_changed(hass, utcnow() + timedelta(seconds=30))
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert hass.states.get(f"{sensor_prefix}_load_avg_15m").state == "1.3"
|
||||
assert hass.states.get(f"{sensor_prefix}_2_4ghz_temperature").state == "40.0"
|
||||
assert not hass.states.get(f"{sensor_prefix}_5ghz_temperature")
|
||||
assert hass.states.get(f"{sensor_prefix}_cpu_temperature").state == "71.2"
|
||||
|
|
Loading…
Reference in New Issue