mirror of
https://github.com/esphome/esphome.git
synced 2025-09-16 18:22:22 +01:00
Add support for Sensor state class (#1835)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
@@ -22,6 +22,7 @@ from esphome.const import (
|
||||
CONF_ON_VALUE_RANGE,
|
||||
CONF_SEND_EVERY,
|
||||
CONF_SEND_FIRST_AT,
|
||||
CONF_STATE_CLASS,
|
||||
CONF_TO,
|
||||
CONF_TRIGGER_ID,
|
||||
CONF_UNIT_OF_MEASUREMENT,
|
||||
@@ -46,6 +47,7 @@ from esphome.const import (
|
||||
DEVICE_CLASS_PRESSURE,
|
||||
DEVICE_CLASS_TIMESTAMP,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
STATE_CLASS_NONE,
|
||||
)
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
from esphome.util import Registry
|
||||
@@ -69,6 +71,14 @@ DEVICE_CLASSES = [
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
]
|
||||
|
||||
sensor_ns = cg.esphome_ns.namespace("sensor")
|
||||
StateClasses = sensor_ns.enum("StateClass")
|
||||
STATE_CLASSES = {
|
||||
"": StateClasses.STATE_CLASS_NONE,
|
||||
"measurement": StateClasses.STATE_CLASS_MEASUREMENT,
|
||||
}
|
||||
validate_state_class = cv.enum(STATE_CLASSES, lower=True, space="_")
|
||||
|
||||
IS_PLATFORM_COMPONENT = True
|
||||
|
||||
|
||||
@@ -157,6 +167,7 @@ SENSOR_SCHEMA = cv.MQTT_COMPONENT_SCHEMA.extend(
|
||||
cv.Optional(CONF_ICON): icon,
|
||||
cv.Optional(CONF_ACCURACY_DECIMALS): accuracy_decimals,
|
||||
cv.Optional(CONF_DEVICE_CLASS): device_class,
|
||||
cv.Optional(CONF_STATE_CLASS): validate_state_class,
|
||||
cv.Optional(CONF_FORCE_UPDATE, default=False): cv.boolean,
|
||||
cv.Optional(CONF_EXPIRE_AFTER): cv.All(
|
||||
cv.requires_component("mqtt"),
|
||||
@@ -190,6 +201,7 @@ def sensor_schema(
|
||||
icon_: str,
|
||||
accuracy_decimals_: int,
|
||||
device_class_: Optional[str] = DEVICE_CLASS_EMPTY,
|
||||
state_class_: Optional[str] = STATE_CLASS_NONE,
|
||||
) -> cv.Schema:
|
||||
schema = SENSOR_SCHEMA
|
||||
if unit_of_measurement_ != UNIT_EMPTY:
|
||||
@@ -214,6 +226,10 @@ def sensor_schema(
|
||||
schema = schema.extend(
|
||||
{cv.Optional(CONF_DEVICE_CLASS, default=device_class_): device_class}
|
||||
)
|
||||
if state_class_ != STATE_CLASS_NONE:
|
||||
schema = schema.extend(
|
||||
{cv.Optional(CONF_STATE_CLASS, default=state_class_): validate_state_class}
|
||||
)
|
||||
return schema
|
||||
|
||||
|
||||
@@ -455,6 +471,8 @@ async def setup_sensor_core_(var, 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_STATE_CLASS in config:
|
||||
cg.add(var.set_state_class(config[CONF_STATE_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:
|
||||
|
@@ -6,6 +6,16 @@ namespace sensor {
|
||||
|
||||
static const char *TAG = "sensor";
|
||||
|
||||
const char *state_class_to_string(StateClass state_class) {
|
||||
switch (state_class) {
|
||||
case STATE_CLASS_MEASUREMENT:
|
||||
return "measurement";
|
||||
case STATE_CLASS_NONE:
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
void Sensor::publish_state(float state) {
|
||||
this->raw_state = state;
|
||||
this->raw_callback_.call(state);
|
||||
@@ -47,6 +57,14 @@ std::string Sensor::get_device_class() {
|
||||
return this->device_class();
|
||||
}
|
||||
std::string Sensor::device_class() { return ""; }
|
||||
void Sensor::set_state_class(StateClass state_class) { this->state_class = state_class; }
|
||||
void Sensor::set_state_class(const std::string &state_class) {
|
||||
if (str_equals_case_insensitive(state_class, "measurement")) {
|
||||
this->state_class = STATE_CLASS_MEASUREMENT;
|
||||
} else {
|
||||
ESP_LOGW(TAG, "'%s' - Unrecognized state class %s", this->get_name().c_str(), state_class.c_str());
|
||||
}
|
||||
}
|
||||
std::string Sensor::get_unit_of_measurement() {
|
||||
if (this->unit_of_measurement_.has_value())
|
||||
return *this->unit_of_measurement_;
|
||||
|
@@ -13,6 +13,7 @@ namespace sensor {
|
||||
if (!obj->get_device_class().empty()) { \
|
||||
ESP_LOGCONFIG(TAG, "%s Device Class: '%s'", prefix, obj->get_device_class().c_str()); \
|
||||
} \
|
||||
ESP_LOGCONFIG(TAG, "%s State Class: '%s'", prefix, state_class_to_string(obj->state_class)); \
|
||||
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()) { \
|
||||
@@ -26,6 +27,16 @@ namespace sensor {
|
||||
} \
|
||||
}
|
||||
|
||||
/**
|
||||
* Sensor state classes
|
||||
*/
|
||||
enum StateClass : uint8_t {
|
||||
STATE_CLASS_NONE = 0,
|
||||
STATE_CLASS_MEASUREMENT = 1,
|
||||
};
|
||||
|
||||
const char *state_class_to_string(StateClass state_class);
|
||||
|
||||
/** Base-class for all sensors.
|
||||
*
|
||||
* A sensor has unit of measurement and can use publish_state to send out a new value with the specified accuracy.
|
||||
@@ -139,6 +150,13 @@ class Sensor : public Nameable {
|
||||
/// Return whether this sensor has gotten a full state (that passed through all filters) yet.
|
||||
bool has_state() const;
|
||||
|
||||
// The state class of this sensor state
|
||||
StateClass state_class{STATE_CLASS_NONE};
|
||||
|
||||
/// Manually set the Home Assistant state class (see sensor::state_class)
|
||||
void set_state_class(StateClass state_class);
|
||||
void set_state_class(const std::string &state_class);
|
||||
|
||||
/** Override this to set the Home Assistant device class for this sensor.
|
||||
*
|
||||
* Return "" to disable this feature.
|
||||
|
Reference in New Issue
Block a user