Add more mysensors sensor attributes (#53566)

This commit is contained in:
Martin Hjelmare 2021-07-27 19:48:37 +02:00 committed by GitHub
parent 27d42e0cd8
commit a133eae88e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 141 additions and 45 deletions

View file

@ -1,16 +1,27 @@
"""Support for MySensors sensors.""" """Support for MySensors sensors."""
from __future__ import annotations from __future__ import annotations
from datetime import datetime
from awesomeversion import AwesomeVersion from awesomeversion import AwesomeVersion
from homeassistant.components import mysensors from homeassistant.components import mysensors
from homeassistant.components.sensor import DOMAIN, SensorEntity from homeassistant.components.sensor import (
DOMAIN,
STATE_CLASS_MEASUREMENT,
SensorEntity,
)
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ( from homeassistant.const import (
CONDUCTIVITY, CONDUCTIVITY,
DEGREE, DEGREE,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_HUMIDITY,
DEVICE_CLASS_ILLUMINANCE,
DEVICE_CLASS_POWER,
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_VOLTAGE,
ELECTRIC_CURRENT_AMPERE, ELECTRIC_CURRENT_AMPERE,
ELECTRIC_POTENTIAL_MILLIVOLT, ELECTRIC_POTENTIAL_MILLIVOLT,
ELECTRIC_POTENTIAL_VOLT, ELECTRIC_POTENTIAL_VOLT,
@ -30,42 +41,68 @@ from homeassistant.const import (
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util.dt import utc_from_timestamp
from .const import MYSENSORS_DISCOVERY, DiscoveryInfo from .const import MYSENSORS_DISCOVERY, DiscoveryInfo
from .helpers import on_unload from .helpers import on_unload
SENSORS: dict[str, list[str | None] | dict[str, list[str | None]]] = { SENSORS: dict[str, list[str | None] | dict[str, list[str | None]]] = {
"V_TEMP": [None, None, DEVICE_CLASS_TEMPERATURE], "V_TEMP": [None, None, DEVICE_CLASS_TEMPERATURE, STATE_CLASS_MEASUREMENT],
"V_HUM": [PERCENTAGE, "mdi:water-percent", DEVICE_CLASS_HUMIDITY], "V_HUM": [
"V_DIMMER": [PERCENTAGE, "mdi:percent", None], PERCENTAGE,
"V_PERCENTAGE": [PERCENTAGE, "mdi:percent", None], "mdi:water-percent",
"V_PRESSURE": [None, "mdi:gauge", None], DEVICE_CLASS_HUMIDITY,
"V_FORECAST": [None, "mdi:weather-partly-cloudy", None], STATE_CLASS_MEASUREMENT,
"V_RAIN": [None, "mdi:weather-rainy", None], ],
"V_RAINRATE": [None, "mdi:weather-rainy", None], "V_DIMMER": [PERCENTAGE, "mdi:percent", None, None],
"V_WIND": [None, "mdi:weather-windy", None], "V_PERCENTAGE": [PERCENTAGE, "mdi:percent", None, None],
"V_GUST": [None, "mdi:weather-windy", None], "V_PRESSURE": [None, "mdi:gauge", None, None],
"V_DIRECTION": [DEGREE, "mdi:compass", None], "V_FORECAST": [None, "mdi:weather-partly-cloudy", None, None],
"V_WEIGHT": [MASS_KILOGRAMS, "mdi:weight-kilogram", None], "V_RAIN": [None, "mdi:weather-rainy", None, None],
"V_DISTANCE": [LENGTH_METERS, "mdi:ruler", None], "V_RAINRATE": [None, "mdi:weather-rainy", None, None],
"V_IMPEDANCE": ["ohm", None, None], "V_WIND": [None, "mdi:weather-windy", None, None],
"V_WATT": [POWER_WATT, None, None], "V_GUST": [None, "mdi:weather-windy", None, None],
"V_KWH": [ENERGY_KILO_WATT_HOUR, None, None], "V_DIRECTION": [DEGREE, "mdi:compass", None, None],
"V_LIGHT_LEVEL": [PERCENTAGE, "mdi:white-balance-sunny", None], "V_WEIGHT": [MASS_KILOGRAMS, "mdi:weight-kilogram", None, None],
"V_FLOW": [LENGTH_METERS, "mdi:gauge", None], "V_DISTANCE": [LENGTH_METERS, "mdi:ruler", None, None],
"V_VOLUME": [f"{VOLUME_CUBIC_METERS}", None, None], "V_IMPEDANCE": ["ohm", None, None, None],
"V_WATT": [POWER_WATT, None, DEVICE_CLASS_POWER, STATE_CLASS_MEASUREMENT],
"V_KWH": [
ENERGY_KILO_WATT_HOUR,
None,
DEVICE_CLASS_ENERGY,
STATE_CLASS_MEASUREMENT,
],
"V_LIGHT_LEVEL": [PERCENTAGE, "mdi:white-balance-sunny", None, None],
"V_FLOW": [LENGTH_METERS, "mdi:gauge", None, None],
"V_VOLUME": [VOLUME_CUBIC_METERS, None, None, None],
"V_LEVEL": { "V_LEVEL": {
"S_SOUND": [SOUND_PRESSURE_DB, "mdi:volume-high", None], "S_SOUND": [SOUND_PRESSURE_DB, "mdi:volume-high", None, None],
"S_VIBRATION": [FREQUENCY_HERTZ, None, None], "S_VIBRATION": [FREQUENCY_HERTZ, None, None, None],
"S_LIGHT_LEVEL": [LIGHT_LUX, "mdi:white-balance-sunny", None], "S_LIGHT_LEVEL": [
LIGHT_LUX,
"mdi:white-balance-sunny",
DEVICE_CLASS_ILLUMINANCE,
STATE_CLASS_MEASUREMENT,
],
}, },
"V_VOLTAGE": [ELECTRIC_POTENTIAL_VOLT, "mdi:flash", None], "V_VOLTAGE": [
"V_CURRENT": [ELECTRIC_CURRENT_AMPERE, "mdi:flash-auto", None], ELECTRIC_POTENTIAL_VOLT,
"V_PH": ["pH", None, None], "mdi:flash",
"V_ORP": [ELECTRIC_POTENTIAL_MILLIVOLT, None, None], DEVICE_CLASS_VOLTAGE,
"V_EC": [CONDUCTIVITY, None, None], STATE_CLASS_MEASUREMENT,
"V_VAR": ["var", None, None], ],
"V_VA": [POWER_VOLT_AMPERE, None, None], "V_CURRENT": [
ELECTRIC_CURRENT_AMPERE,
"mdi:flash-auto",
DEVICE_CLASS_CURRENT,
STATE_CLASS_MEASUREMENT,
],
"V_PH": ["pH", None, None, None],
"V_ORP": [ELECTRIC_POTENTIAL_MILLIVOLT, None, None, None],
"V_EC": [CONDUCTIVITY, None, None, None],
"V_VAR": ["var", None, None, None],
"V_VA": [POWER_VOLT_AMPERE, None, None, None],
} }
@ -124,6 +161,20 @@ class MySensorsSensor(mysensors.device.MySensorsEntity, SensorEntity):
"""Return the icon to use in the frontend, if any.""" """Return the icon to use in the frontend, if any."""
return self._get_sensor_type()[1] return self._get_sensor_type()[1]
@property
def last_reset(self) -> datetime | None:
"""Return the time when the sensor was last reset, if any."""
set_req = self.gateway.const.SetReq
if set_req(self.value_type).name == "V_KWH":
return utc_from_timestamp(0)
return None
@property
def state_class(self) -> str | None:
"""Return the state class of this entity."""
return self._get_sensor_type()[3]
@property @property
def unit_of_measurement(self) -> str | None: def unit_of_measurement(self) -> str | None:
"""Return the unit of measurement of this entity.""" """Return the unit of measurement of this entity."""
@ -148,10 +199,12 @@ class MySensorsSensor(mysensors.device.MySensorsEntity, SensorEntity):
pres = self.gateway.const.Presentation pres = self.gateway.const.Presentation
set_req = self.gateway.const.SetReq set_req = self.gateway.const.SetReq
_sensor_type = SENSORS.get(set_req(self.value_type).name, [None, None, None]) _sensor_type = SENSORS.get(
set_req(self.value_type).name, [None, None, None, None]
)
if isinstance(_sensor_type, dict): if isinstance(_sensor_type, dict):
sensor_type = _sensor_type.get( sensor_type = _sensor_type.get(
pres(self.child_type).name, [None, None, None] pres(self.child_type).name, [None, None, None, None]
) )
else: else:
sensor_type = _sensor_type sensor_type = _sensor_type

View file

@ -1,7 +1,6 @@
"""Support for MySensors switches.""" """Support for MySensors switches."""
from __future__ import annotations from __future__ import annotations
from contextlib import suppress
from typing import Any from typing import Any
import voluptuous as vol import voluptuous as vol
@ -109,18 +108,6 @@ async def async_setup_entry(
class MySensorsSwitch(mysensors.device.MySensorsEntity, SwitchEntity): class MySensorsSwitch(mysensors.device.MySensorsEntity, SwitchEntity):
"""Representation of the value of a MySensors Switch child node.""" """Representation of the value of a MySensors Switch child node."""
@property
def current_power_w(self) -> float | None:
"""Return the current power usage in W."""
set_req = self.gateway.const.SetReq
value = self._values.get(set_req.V_WATT)
float_value: float | None = None
if value is not None:
with suppress(ValueError):
float_value = float(value)
return float_value
@property @property
def is_on(self) -> bool: def is_on(self) -> bool:
"""Return True if switch is on.""" """Return True if switch is on."""

View file

@ -156,3 +156,17 @@ def gps_sensor(gateway_nodes, gps_sensor_state) -> Sensor:
nodes = update_gateway_nodes(gateway_nodes, gps_sensor_state) nodes = update_gateway_nodes(gateway_nodes, gps_sensor_state)
node = nodes[1] node = nodes[1]
return node return node
@pytest.fixture(name="power_sensor_state", scope="session")
def power_sensor_state_fixture() -> dict:
"""Load the power sensor state."""
return load_nodes_state("mysensors/power_sensor_state.json")
@pytest.fixture
def power_sensor(gateway_nodes, power_sensor_state) -> Sensor:
"""Load the power sensor."""
nodes = update_gateway_nodes(gateway_nodes, power_sensor_state)
node = nodes[1]
return node

View file

@ -1,6 +1,15 @@
"""Provide tests for mysensors sensor platform.""" """Provide tests for mysensors sensor platform."""
from homeassistant.components.sensor import ATTR_STATE_CLASS, STATE_CLASS_MEASUREMENT
from homeassistant.const import (
ATTR_DEVICE_CLASS,
ATTR_UNIT_OF_MEASUREMENT,
DEVICE_CLASS_POWER,
POWER_WATT,
)
async def test_gps_sensor(hass, gps_sensor, integration): async def test_gps_sensor(hass, gps_sensor, integration):
"""Test a gps sensor.""" """Test a gps sensor."""
entity_id = "sensor.gps_sensor_1_1" entity_id = "sensor.gps_sensor_1_1"
@ -8,3 +17,15 @@ async def test_gps_sensor(hass, gps_sensor, integration):
state = hass.states.get(entity_id) state = hass.states.get(entity_id)
assert state.state == "40.741894,-73.989311,12" assert state.state == "40.741894,-73.989311,12"
async def test_power_sensor(hass, power_sensor, integration):
"""Test a power sensor."""
entity_id = "sensor.power_sensor_1_1"
state = hass.states.get(entity_id)
assert state.state == "1200"
assert state.attributes[ATTR_DEVICE_CLASS] == DEVICE_CLASS_POWER
assert state.attributes[ATTR_UNIT_OF_MEASUREMENT] == POWER_WATT
assert state.attributes[ATTR_STATE_CLASS] == STATE_CLASS_MEASUREMENT

View file

@ -0,0 +1,21 @@
{
"1": {
"sensor_id": 1,
"children": {
"1": {
"id": 1,
"type": 13,
"description": "",
"values": {
"17": "1200"
}
}
},
"type": 17,
"sketch_name": "Power Sensor",
"sketch_version": "1.0",
"battery_level": 0,
"protocol_version": "2.3.2",
"heartbeat": 0
}
}