Extract smartthings switch energy attributes into sensors (#53719)
This commit is contained in:
parent
8972fae0ca
commit
2b2cddb5f0
5 changed files with 257 additions and 75 deletions
|
@ -3,18 +3,26 @@ from __future__ import annotations
|
|||
|
||||
from collections import namedtuple
|
||||
from collections.abc import Sequence
|
||||
from datetime import datetime
|
||||
|
||||
from pysmartthings import Attribute, Capability
|
||||
from pysmartthings.device import DeviceEntity
|
||||
|
||||
from homeassistant.components.sensor import SensorEntity
|
||||
from homeassistant.components.sensor import STATE_CLASS_MEASUREMENT, SensorEntity
|
||||
from homeassistant.const import (
|
||||
AREA_SQUARE_METERS,
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
DEVICE_CLASS_BATTERY,
|
||||
DEVICE_CLASS_CO,
|
||||
DEVICE_CLASS_CO2,
|
||||
DEVICE_CLASS_ENERGY,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
DEVICE_CLASS_ILLUMINANCE,
|
||||
DEVICE_CLASS_POWER,
|
||||
DEVICE_CLASS_SIGNAL_STRENGTH,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
DEVICE_CLASS_TIMESTAMP,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
ELECTRIC_POTENTIAL_VOLT,
|
||||
ENERGY_KILO_WATT_HOUR,
|
||||
LIGHT_LUX,
|
||||
|
@ -25,26 +33,27 @@ from homeassistant.const import (
|
|||
TEMP_FAHRENHEIT,
|
||||
VOLUME_CUBIC_METERS,
|
||||
)
|
||||
from homeassistant.util.dt import utc_from_timestamp
|
||||
|
||||
from . import SmartThingsEntity
|
||||
from .const import DATA_BROKERS, DOMAIN
|
||||
|
||||
Map = namedtuple("map", "attribute name default_unit device_class")
|
||||
Map = namedtuple("map", "attribute name default_unit device_class state_class")
|
||||
|
||||
CAPABILITY_TO_SENSORS = {
|
||||
Capability.activity_lighting_mode: [
|
||||
Map(Attribute.lighting_mode, "Activity Lighting Mode", None, None)
|
||||
Map(Attribute.lighting_mode, "Activity Lighting Mode", None, None, None)
|
||||
],
|
||||
Capability.air_conditioner_mode: [
|
||||
Map(Attribute.air_conditioner_mode, "Air Conditioner Mode", None, None)
|
||||
Map(Attribute.air_conditioner_mode, "Air Conditioner Mode", None, None, None)
|
||||
],
|
||||
Capability.air_quality_sensor: [
|
||||
Map(Attribute.air_quality, "Air Quality", "CAQI", None)
|
||||
Map(Attribute.air_quality, "Air Quality", "CAQI", None, STATE_CLASS_MEASUREMENT)
|
||||
],
|
||||
Capability.alarm: [Map(Attribute.alarm, "Alarm", None, None)],
|
||||
Capability.audio_volume: [Map(Attribute.volume, "Volume", PERCENTAGE, None)],
|
||||
Capability.alarm: [Map(Attribute.alarm, "Alarm", None, None, None)],
|
||||
Capability.audio_volume: [Map(Attribute.volume, "Volume", PERCENTAGE, None, None)],
|
||||
Capability.battery: [
|
||||
Map(Attribute.battery, "Battery", PERCENTAGE, DEVICE_CLASS_BATTERY)
|
||||
Map(Attribute.battery, "Battery", PERCENTAGE, DEVICE_CLASS_BATTERY, None)
|
||||
],
|
||||
Capability.body_mass_index_measurement: [
|
||||
Map(
|
||||
|
@ -52,57 +61,80 @@ CAPABILITY_TO_SENSORS = {
|
|||
"Body Mass Index",
|
||||
f"{MASS_KILOGRAMS}/{AREA_SQUARE_METERS}",
|
||||
None,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
],
|
||||
Capability.body_weight_measurement: [
|
||||
Map(Attribute.body_weight_measurement, "Body Weight", MASS_KILOGRAMS, None)
|
||||
Map(
|
||||
Attribute.body_weight_measurement,
|
||||
"Body Weight",
|
||||
MASS_KILOGRAMS,
|
||||
None,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
],
|
||||
Capability.carbon_dioxide_measurement: [
|
||||
Map(
|
||||
Attribute.carbon_dioxide,
|
||||
"Carbon Dioxide Measurement",
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
None,
|
||||
DEVICE_CLASS_CO2,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
],
|
||||
Capability.carbon_monoxide_detector: [
|
||||
Map(Attribute.carbon_monoxide, "Carbon Monoxide Detector", None, None)
|
||||
Map(Attribute.carbon_monoxide, "Carbon Monoxide Detector", None, None, None)
|
||||
],
|
||||
Capability.carbon_monoxide_measurement: [
|
||||
Map(
|
||||
Attribute.carbon_monoxide_level,
|
||||
"Carbon Monoxide Measurement",
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
None,
|
||||
DEVICE_CLASS_CO,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
],
|
||||
Capability.dishwasher_operating_state: [
|
||||
Map(Attribute.machine_state, "Dishwasher Machine State", None, None),
|
||||
Map(Attribute.dishwasher_job_state, "Dishwasher Job State", None, None),
|
||||
Map(Attribute.machine_state, "Dishwasher Machine State", None, None, None),
|
||||
Map(Attribute.dishwasher_job_state, "Dishwasher Job State", None, None, None),
|
||||
Map(
|
||||
Attribute.completion_time,
|
||||
"Dishwasher Completion Time",
|
||||
None,
|
||||
DEVICE_CLASS_TIMESTAMP,
|
||||
None,
|
||||
),
|
||||
],
|
||||
Capability.dryer_mode: [Map(Attribute.dryer_mode, "Dryer Mode", None, None)],
|
||||
Capability.dryer_mode: [Map(Attribute.dryer_mode, "Dryer Mode", None, None, None)],
|
||||
Capability.dryer_operating_state: [
|
||||
Map(Attribute.machine_state, "Dryer Machine State", None, None),
|
||||
Map(Attribute.dryer_job_state, "Dryer Job State", None, None),
|
||||
Map(Attribute.machine_state, "Dryer Machine State", None, None, None),
|
||||
Map(Attribute.dryer_job_state, "Dryer Job State", None, None, None),
|
||||
Map(
|
||||
Attribute.completion_time,
|
||||
"Dryer Completion Time",
|
||||
None,
|
||||
DEVICE_CLASS_TIMESTAMP,
|
||||
None,
|
||||
),
|
||||
],
|
||||
Capability.dust_sensor: [
|
||||
Map(Attribute.fine_dust_level, "Fine Dust Level", None, None),
|
||||
Map(Attribute.dust_level, "Dust Level", None, None),
|
||||
Map(
|
||||
Attribute.fine_dust_level,
|
||||
"Fine Dust Level",
|
||||
None,
|
||||
None,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
Map(Attribute.dust_level, "Dust Level", None, None, STATE_CLASS_MEASUREMENT),
|
||||
],
|
||||
Capability.energy_meter: [
|
||||
Map(Attribute.energy, "Energy Meter", ENERGY_KILO_WATT_HOUR, None)
|
||||
Map(
|
||||
Attribute.energy,
|
||||
"Energy Meter",
|
||||
ENERGY_KILO_WATT_HOUR,
|
||||
DEVICE_CLASS_ENERGY,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
],
|
||||
Capability.equivalent_carbon_dioxide_measurement: [
|
||||
Map(
|
||||
|
@ -110,6 +142,7 @@ CAPABILITY_TO_SENSORS = {
|
|||
"Equivalent Carbon Dioxide Measurement",
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
None,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
],
|
||||
Capability.formaldehyde_measurement: [
|
||||
|
@ -118,50 +151,94 @@ CAPABILITY_TO_SENSORS = {
|
|||
"Formaldehyde Measurement",
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
None,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
],
|
||||
Capability.gas_meter: [
|
||||
Map(Attribute.gas_meter, "Gas Meter", ENERGY_KILO_WATT_HOUR, None),
|
||||
Map(Attribute.gas_meter_calorific, "Gas Meter Calorific", None, None),
|
||||
Map(Attribute.gas_meter_time, "Gas Meter Time", None, DEVICE_CLASS_TIMESTAMP),
|
||||
Map(Attribute.gas_meter_volume, "Gas Meter Volume", VOLUME_CUBIC_METERS, None),
|
||||
Map(
|
||||
Attribute.gas_meter,
|
||||
"Gas Meter",
|
||||
ENERGY_KILO_WATT_HOUR,
|
||||
None,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
Map(Attribute.gas_meter_calorific, "Gas Meter Calorific", None, None, None),
|
||||
Map(
|
||||
Attribute.gas_meter_time,
|
||||
"Gas Meter Time",
|
||||
None,
|
||||
DEVICE_CLASS_TIMESTAMP,
|
||||
None,
|
||||
),
|
||||
Map(
|
||||
Attribute.gas_meter_volume,
|
||||
"Gas Meter Volume",
|
||||
VOLUME_CUBIC_METERS,
|
||||
None,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
],
|
||||
Capability.illuminance_measurement: [
|
||||
Map(Attribute.illuminance, "Illuminance", LIGHT_LUX, DEVICE_CLASS_ILLUMINANCE)
|
||||
Map(
|
||||
Attribute.illuminance,
|
||||
"Illuminance",
|
||||
LIGHT_LUX,
|
||||
DEVICE_CLASS_ILLUMINANCE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
],
|
||||
Capability.infrared_level: [
|
||||
Map(Attribute.infrared_level, "Infrared Level", PERCENTAGE, None)
|
||||
Map(
|
||||
Attribute.infrared_level,
|
||||
"Infrared Level",
|
||||
PERCENTAGE,
|
||||
None,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
],
|
||||
Capability.media_input_source: [
|
||||
Map(Attribute.input_source, "Media Input Source", None, None)
|
||||
Map(Attribute.input_source, "Media Input Source", None, None, None)
|
||||
],
|
||||
Capability.media_playback_repeat: [
|
||||
Map(Attribute.playback_repeat_mode, "Media Playback Repeat", None, None)
|
||||
Map(Attribute.playback_repeat_mode, "Media Playback Repeat", None, None, None)
|
||||
],
|
||||
Capability.media_playback_shuffle: [
|
||||
Map(Attribute.playback_shuffle, "Media Playback Shuffle", None, None)
|
||||
Map(Attribute.playback_shuffle, "Media Playback Shuffle", None, None, None)
|
||||
],
|
||||
Capability.media_playback: [
|
||||
Map(Attribute.playback_status, "Media Playback Status", None, None)
|
||||
Map(Attribute.playback_status, "Media Playback Status", None, None, None)
|
||||
],
|
||||
Capability.odor_sensor: [Map(Attribute.odor_level, "Odor Sensor", None, None)],
|
||||
Capability.oven_mode: [Map(Attribute.oven_mode, "Oven Mode", None, None)],
|
||||
Capability.odor_sensor: [
|
||||
Map(Attribute.odor_level, "Odor Sensor", None, None, None)
|
||||
],
|
||||
Capability.oven_mode: [Map(Attribute.oven_mode, "Oven Mode", None, None, None)],
|
||||
Capability.oven_operating_state: [
|
||||
Map(Attribute.machine_state, "Oven Machine State", None, None),
|
||||
Map(Attribute.oven_job_state, "Oven Job State", None, None),
|
||||
Map(Attribute.completion_time, "Oven Completion Time", None, None),
|
||||
Map(Attribute.machine_state, "Oven Machine State", None, None, None),
|
||||
Map(Attribute.oven_job_state, "Oven Job State", None, None, None),
|
||||
Map(Attribute.completion_time, "Oven Completion Time", None, None, None),
|
||||
],
|
||||
Capability.oven_setpoint: [
|
||||
Map(Attribute.oven_setpoint, "Oven Set Point", None, None)
|
||||
Map(Attribute.oven_setpoint, "Oven Set Point", None, None, None)
|
||||
],
|
||||
Capability.power_meter: [
|
||||
Map(
|
||||
Attribute.power,
|
||||
"Power Meter",
|
||||
POWER_WATT,
|
||||
DEVICE_CLASS_POWER,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
],
|
||||
Capability.power_source: [
|
||||
Map(Attribute.power_source, "Power Source", None, None, None)
|
||||
],
|
||||
Capability.power_meter: [Map(Attribute.power, "Power Meter", POWER_WATT, None)],
|
||||
Capability.power_source: [Map(Attribute.power_source, "Power Source", None, None)],
|
||||
Capability.refrigeration_setpoint: [
|
||||
Map(
|
||||
Attribute.refrigeration_setpoint,
|
||||
"Refrigeration Setpoint",
|
||||
None,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
None,
|
||||
)
|
||||
],
|
||||
Capability.relative_humidity_measurement: [
|
||||
|
@ -170,6 +247,7 @@ CAPABILITY_TO_SENSORS = {
|
|||
"Relative Humidity Measurement",
|
||||
PERCENTAGE,
|
||||
DEVICE_CLASS_HUMIDITY,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
],
|
||||
Capability.robot_cleaner_cleaning_mode: [
|
||||
|
@ -178,25 +256,43 @@ CAPABILITY_TO_SENSORS = {
|
|||
"Robot Cleaner Cleaning Mode",
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
],
|
||||
Capability.robot_cleaner_movement: [
|
||||
Map(Attribute.robot_cleaner_movement, "Robot Cleaner Movement", None, None)
|
||||
Map(
|
||||
Attribute.robot_cleaner_movement, "Robot Cleaner Movement", None, None, None
|
||||
)
|
||||
],
|
||||
Capability.robot_cleaner_turbo_mode: [
|
||||
Map(Attribute.robot_cleaner_turbo_mode, "Robot Cleaner Turbo Mode", None, None)
|
||||
Map(
|
||||
Attribute.robot_cleaner_turbo_mode,
|
||||
"Robot Cleaner Turbo Mode",
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
],
|
||||
Capability.signal_strength: [
|
||||
Map(Attribute.lqi, "LQI Signal Strength", None, None),
|
||||
Map(Attribute.rssi, "RSSI Signal Strength", None, None),
|
||||
Map(Attribute.lqi, "LQI Signal Strength", None, None, STATE_CLASS_MEASUREMENT),
|
||||
Map(
|
||||
Attribute.rssi,
|
||||
"RSSI Signal Strength",
|
||||
None,
|
||||
DEVICE_CLASS_SIGNAL_STRENGTH,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
),
|
||||
],
|
||||
Capability.smoke_detector: [
|
||||
Map(Attribute.smoke, "Smoke Detector", None, None, None)
|
||||
],
|
||||
Capability.smoke_detector: [Map(Attribute.smoke, "Smoke Detector", None, None)],
|
||||
Capability.temperature_measurement: [
|
||||
Map(
|
||||
Attribute.temperature,
|
||||
"Temperature Measurement",
|
||||
None,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
],
|
||||
Capability.thermostat_cooling_setpoint: [
|
||||
|
@ -205,10 +301,11 @@ CAPABILITY_TO_SENSORS = {
|
|||
"Thermostat Cooling Setpoint",
|
||||
None,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
None,
|
||||
)
|
||||
],
|
||||
Capability.thermostat_fan_mode: [
|
||||
Map(Attribute.thermostat_fan_mode, "Thermostat Fan Mode", None, None)
|
||||
Map(Attribute.thermostat_fan_mode, "Thermostat Fan Mode", None, None, None)
|
||||
],
|
||||
Capability.thermostat_heating_setpoint: [
|
||||
Map(
|
||||
|
@ -216,10 +313,11 @@ CAPABILITY_TO_SENSORS = {
|
|||
"Thermostat Heating Setpoint",
|
||||
None,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
None,
|
||||
)
|
||||
],
|
||||
Capability.thermostat_mode: [
|
||||
Map(Attribute.thermostat_mode, "Thermostat Mode", None, None)
|
||||
Map(Attribute.thermostat_mode, "Thermostat Mode", None, None, None)
|
||||
],
|
||||
Capability.thermostat_operating_state: [
|
||||
Map(
|
||||
|
@ -227,6 +325,7 @@ CAPABILITY_TO_SENSORS = {
|
|||
"Thermostat Operating State",
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
],
|
||||
Capability.thermostat_setpoint: [
|
||||
|
@ -235,12 +334,13 @@ CAPABILITY_TO_SENSORS = {
|
|||
"Thermostat Setpoint",
|
||||
None,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
None,
|
||||
)
|
||||
],
|
||||
Capability.three_axis: [],
|
||||
Capability.tv_channel: [
|
||||
Map(Attribute.tv_channel, "Tv Channel", None, None),
|
||||
Map(Attribute.tv_channel_name, "Tv Channel Name", None, None),
|
||||
Map(Attribute.tv_channel, "Tv Channel", None, None, None),
|
||||
Map(Attribute.tv_channel_name, "Tv Channel Name", None, None, None),
|
||||
],
|
||||
Capability.tvoc_measurement: [
|
||||
Map(
|
||||
|
@ -248,23 +348,39 @@ CAPABILITY_TO_SENSORS = {
|
|||
"Tvoc Measurement",
|
||||
CONCENTRATION_PARTS_PER_MILLION,
|
||||
None,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
],
|
||||
Capability.ultraviolet_index: [
|
||||
Map(Attribute.ultraviolet_index, "Ultraviolet Index", None, None)
|
||||
Map(
|
||||
Attribute.ultraviolet_index,
|
||||
"Ultraviolet Index",
|
||||
None,
|
||||
None,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
],
|
||||
Capability.voltage_measurement: [
|
||||
Map(Attribute.voltage, "Voltage Measurement", ELECTRIC_POTENTIAL_VOLT, None)
|
||||
Map(
|
||||
Attribute.voltage,
|
||||
"Voltage Measurement",
|
||||
ELECTRIC_POTENTIAL_VOLT,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
],
|
||||
Capability.washer_mode: [
|
||||
Map(Attribute.washer_mode, "Washer Mode", None, None, None)
|
||||
],
|
||||
Capability.washer_mode: [Map(Attribute.washer_mode, "Washer Mode", None, None)],
|
||||
Capability.washer_operating_state: [
|
||||
Map(Attribute.machine_state, "Washer Machine State", None, None),
|
||||
Map(Attribute.washer_job_state, "Washer Job State", None, None),
|
||||
Map(Attribute.machine_state, "Washer Machine State", None, None, None),
|
||||
Map(Attribute.washer_job_state, "Washer Job State", None, None, None),
|
||||
Map(
|
||||
Attribute.completion_time,
|
||||
"Washer Completion Time",
|
||||
None,
|
||||
DEVICE_CLASS_TIMESTAMP,
|
||||
None,
|
||||
),
|
||||
],
|
||||
}
|
||||
|
@ -292,11 +408,34 @@ async def async_setup_entry(hass, config_entry, async_add_entities):
|
|||
sensors.extend(
|
||||
[
|
||||
SmartThingsSensor(
|
||||
device, m.attribute, m.name, m.default_unit, m.device_class
|
||||
device,
|
||||
m.attribute,
|
||||
m.name,
|
||||
m.default_unit,
|
||||
m.device_class,
|
||||
m.state_class,
|
||||
)
|
||||
for m in maps
|
||||
]
|
||||
)
|
||||
|
||||
if broker.any_assigned(device.device_id, "switch"):
|
||||
for capability in (Capability.energy_meter, Capability.power_meter):
|
||||
maps = CAPABILITY_TO_SENSORS[capability]
|
||||
sensors.extend(
|
||||
[
|
||||
SmartThingsSensor(
|
||||
device,
|
||||
m.attribute,
|
||||
m.name,
|
||||
m.default_unit,
|
||||
m.device_class,
|
||||
m.state_class,
|
||||
)
|
||||
for m in maps
|
||||
]
|
||||
)
|
||||
|
||||
async_add_entities(sensors)
|
||||
|
||||
|
||||
|
@ -311,14 +450,21 @@ class SmartThingsSensor(SmartThingsEntity, SensorEntity):
|
|||
"""Define a SmartThings Sensor."""
|
||||
|
||||
def __init__(
|
||||
self, device, attribute: str, name: str, default_unit: str, device_class: str
|
||||
):
|
||||
self,
|
||||
device: DeviceEntity,
|
||||
attribute: str,
|
||||
name: str,
|
||||
default_unit: str,
|
||||
device_class: str,
|
||||
state_class: str | None,
|
||||
) -> None:
|
||||
"""Init the class."""
|
||||
super().__init__(device)
|
||||
self._attribute = attribute
|
||||
self._name = name
|
||||
self._device_class = device_class
|
||||
self._default_unit = default_unit
|
||||
self._attr_state_class = state_class
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
|
@ -346,6 +492,13 @@ class SmartThingsSensor(SmartThingsEntity, SensorEntity):
|
|||
unit = self._device.status.attributes[self._attribute].unit
|
||||
return UNITS.get(unit, unit) if unit else self._default_unit
|
||||
|
||||
@property
|
||||
def last_reset(self) -> datetime | None:
|
||||
"""Return the time when the sensor was last reset, if any."""
|
||||
if self._attribute == Attribute.energy:
|
||||
return utc_from_timestamp(0)
|
||||
return None
|
||||
|
||||
|
||||
class SmartThingsThreeAxisSensor(SmartThingsEntity, SensorEntity):
|
||||
"""Define a SmartThings Three Axis Sensor."""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue