1
0
mirror of https://github.com/esphome/esphome.git synced 2025-09-17 02:32:20 +01:00

Device class attribute for sensor component (#1525)

* Add constants for sensor device_class

* Add device_class attribute to sensor component

* Add device_class attribute to sensor class

* Add device_class to mhz19 temperature sensor

* Add device_class to sensor in api component

* Add test for device_class of sensor

* Rename DEVICE_CLASS_NONE to DEVICE_CLASS_EMPTY for consistency

* Make optional attributes of sensor component truly optional
This commit is contained in:
marecabo
2021-02-15 16:49:02 +01:00
committed by GitHub
parent a342302114
commit f95be6a0df
11 changed files with 117 additions and 12 deletions

View File

@@ -4,15 +4,25 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.components import mqtt
from esphome.const import CONF_ABOVE, CONF_ACCURACY_DECIMALS, CONF_ALPHA, CONF_BELOW, \
CONF_EXPIRE_AFTER, CONF_FILTERS, CONF_FROM, CONF_ICON, CONF_ID, CONF_INTERNAL, \
from esphome.const import CONF_DEVICE_CLASS, CONF_ABOVE, CONF_ACCURACY_DECIMALS, CONF_ALPHA, \
CONF_BELOW, CONF_EXPIRE_AFTER, CONF_FILTERS, CONF_FROM, CONF_ICON, CONF_ID, CONF_INTERNAL, \
CONF_ON_RAW_VALUE, CONF_ON_VALUE, CONF_ON_VALUE_RANGE, CONF_SEND_EVERY, CONF_SEND_FIRST_AT, \
CONF_TO, CONF_TRIGGER_ID, CONF_UNIT_OF_MEASUREMENT, CONF_WINDOW_SIZE, CONF_NAME, CONF_MQTT_ID, \
CONF_FORCE_UPDATE
CONF_FORCE_UPDATE, UNIT_EMPTY, ICON_EMPTY, DEVICE_CLASS_EMPTY, DEVICE_CLASS_BATTERY, \
DEVICE_CLASS_CURRENT, DEVICE_CLASS_ENERGY, DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE, \
DEVICE_CLASS_SIGNAL_STRENGTH, DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_POWER, \
DEVICE_CLASS_POWER_FACTOR, DEVICE_CLASS_PRESSURE, DEVICE_CLASS_TIMESTAMP, DEVICE_CLASS_VOLTAGE
from esphome.core import CORE, coroutine, coroutine_with_priority
from esphome.util import Registry
CODEOWNERS = ['@esphome/core']
DEVICE_CLASSES = [
DEVICE_CLASS_EMPTY, DEVICE_CLASS_BATTERY, DEVICE_CLASS_CURRENT, DEVICE_CLASS_ENERGY,
DEVICE_CLASS_HUMIDITY, DEVICE_CLASS_ILLUMINANCE, DEVICE_CLASS_SIGNAL_STRENGTH,
DEVICE_CLASS_TEMPERATURE, DEVICE_CLASS_POWER, DEVICE_CLASS_POWER_FACTOR, DEVICE_CLASS_PRESSURE,
DEVICE_CLASS_TIMESTAMP, DEVICE_CLASS_VOLTAGE
]
IS_PLATFORM_COMPONENT = True
@@ -80,6 +90,7 @@ SensorInRangeCondition = sensor_ns.class_('SensorInRangeCondition', Filter)
unit_of_measurement = cv.string_strict
accuracy_decimals = cv.int_
icon = cv.icon
device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space='_')
SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({
cv.OnlyWith(CONF_MQTT_ID, 'mqtt'): cv.declare_id(mqtt.MQTTSensorComponent),
@@ -87,6 +98,7 @@ SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({
cv.Optional(CONF_UNIT_OF_MEASUREMENT): unit_of_measurement,
cv.Optional(CONF_ICON): icon,
cv.Optional(CONF_ACCURACY_DECIMALS): accuracy_decimals,
cv.Optional(CONF_DEVICE_CLASS): device_class,
cv.Optional(CONF_FORCE_UPDATE, default=False): cv.boolean,
cv.Optional(CONF_EXPIRE_AFTER): cv.All(cv.requires_component('mqtt'),
cv.Any(None, cv.positive_time_period_milliseconds)),
@@ -105,13 +117,25 @@ SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend({
})
def sensor_schema(unit_of_measurement_, icon_, accuracy_decimals_):
# type: (str, str, int) -> cv.Schema
return SENSOR_SCHEMA.extend({
cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=unit_of_measurement_): unit_of_measurement,
cv.Optional(CONF_ICON, default=icon_): icon,
cv.Optional(CONF_ACCURACY_DECIMALS, default=accuracy_decimals_): accuracy_decimals,
})
def sensor_schema(unit_of_measurement_=UNIT_EMPTY, icon_=ICON_EMPTY, accuracy_decimals_=0,
device_class_=DEVICE_CLASS_EMPTY):
# type: (str, str, int, str) -> cv.Schema
schema = SENSOR_SCHEMA
if unit_of_measurement_ != UNIT_EMPTY:
schema = schema.extend({
cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=unit_of_measurement_): unit_of_measurement
})
if icon_ != ICON_EMPTY:
schema = schema.extend({cv.Optional(CONF_ICON, default=icon_): icon})
if accuracy_decimals_ != 0:
schema = schema.extend({
cv.Optional(CONF_ACCURACY_DECIMALS, default=accuracy_decimals_): accuracy_decimals,
})
if device_class_ != DEVICE_CLASS_EMPTY:
schema = schema.extend({
cv.Optional(CONF_DEVICE_CLASS, default=device_class_): device_class
})
return schema
@FILTER_REGISTRY.register('offset', OffsetFilter, cv.float_)
@@ -253,6 +277,8 @@ def setup_sensor_core_(var, config):
cg.add(var.set_name(config[CONF_NAME]))
if CONF_INTERNAL in config:
cg.add(var.set_internal(config[CONF_INTERNAL]))
if CONF_DEVICE_CLASS in config:
cg.add(var.set_device_class(config[CONF_DEVICE_CLASS]))
if CONF_UNIT_OF_MEASUREMENT in config:
cg.add(var.set_unit_of_measurement(config[CONF_UNIT_OF_MEASUREMENT]))
if CONF_ICON in config:

View File

@@ -40,6 +40,13 @@ std::string Sensor::get_icon() {
return *this->icon_;
return this->icon();
}
void Sensor::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
std::string Sensor::get_device_class() {
if (this->device_class_.has_value())
return *this->device_class_;
return this->device_class();
}
std::string Sensor::device_class() { return ""; }
std::string Sensor::get_unit_of_measurement() {
if (this->unit_of_measurement_.has_value())
return *this->unit_of_measurement_;

View File

@@ -10,6 +10,9 @@ namespace sensor {
#define LOG_SENSOR(prefix, type, obj) \
if (obj != nullptr) { \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, type, obj->get_name().c_str()); \
if (!obj->get_device_class().empty()) { \
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, obj->get_device_class().c_str()); \
} \
ESP_LOGCONFIG(TAG, "%s Unit of Measurement: '%s'", prefix, obj->get_unit_of_measurement().c_str()); \
ESP_LOGCONFIG(TAG, "%s Accuracy Decimals: %d", prefix, obj->get_accuracy_decimals()); \
if (!obj->get_icon().empty()) { \
@@ -122,6 +125,12 @@ class Sensor : public Nameable {
*/
float state;
/// Manually set the Home Assistant device class (see sensor::device_class)
void set_device_class(const std::string &device_class);
/// Get the device class for this sensor, using the manual override if specified.
std::string get_device_class();
/** This member variable stores the current raw state of the sensor. Unlike .state,
* this will be updated immediately when publish_state is called.
*/
@@ -130,6 +139,14 @@ class Sensor : public Nameable {
/// Return whether this sensor has gotten a full state (that passed through all filters) yet.
bool has_state() const;
/** Override this to set the Home Assistant device class for this sensor.
*
* Return "" to disable this feature.
*
* @return The device class of this sensor, for example "temperature".
*/
virtual std::string device_class();
/** A unique ID for this sensor, empty for no unique id. See unique ID requirements:
* https://developers.home-assistant.io/docs/en/entity_registry_index.html#unique-id-requirements
*
@@ -174,6 +191,8 @@ class Sensor : public Nameable {
/// Return the accuracy in decimals for this sensor.
virtual int8_t accuracy_decimals(); // NOLINT
optional<std::string> device_class_{}; ///< Stores the override of the device class
uint32_t hash_base() override;
CallbackManager<void(float)> raw_callback_; ///< Storage for raw state callbacks.