EntityBase Refactor (#2418)

* Renamed Nameable to EntityBase (cpp)

* Renamed NAMEABLE_SCHEMA to ENTITY_BASE_SCHEMA (Python)

* Renamed cg.Nameable to cg.EntityBase (Python)

* Remove redundant use of CONF_NAME from esp32_touch

* Remove redundant use of CONF_NAME from mcp3008

* Updated test

* Moved EntityBase from Component.h and Component.cpp

* Added icon property to EntityBase

* Added CONF_ICON to ENTITY_BASE_SCHEMA and added setup_entity function to cpp_helpers

* Added MQTT component getters for icon and disabled_by_default

* Lint

* Removed icon field from MQTT components

* Code generation now uses setup_entity to setENTITY_BASE_SCHEMA fields

* Removed unused import

* Added cstdint include

* Optimisation: don't set icon if it is empty

* Remove icon from NumberTraits and SelectTraits

* Removed unused import

* Integration and Total Daily Energy sensors now inherit icons from their parents during code generation

* Minor comment correction

* Removed redundant icon-handling code from sensor, switch, and text_sensor

* Update esphome/components/tsl2591/tsl2591.h

Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>

* Added icon property to binary sensor, climate, cover, and fan component tests

* Added icons for Binary Sensor, Climate, Cover, Fan, and Light  to API

* Consolidated EntityBase fields in MQTT components

Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl>
This commit is contained in:
Paul Monigatti 2021-10-10 21:37:05 +13:00 committed by GitHub
parent 92b85f98e8
commit 471b82f727
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
83 changed files with 395 additions and 351 deletions

View file

@ -67,7 +67,7 @@ from esphome.cpp_types import ( # noqa
NAN,
esphome_ns,
App,
Nameable,
EntityBase,
Component,
ComponentPtr,
PollingComponent,

View file

@ -215,6 +215,7 @@ message ListEntitiesBinarySensorResponse {
string device_class = 5;
bool is_status_binary_sensor = 6;
bool disabled_by_default = 7;
string icon = 8;
}
message BinarySensorStateResponse {
option (id) = 21;
@ -245,6 +246,7 @@ message ListEntitiesCoverResponse {
bool supports_tilt = 7;
string device_class = 8;
bool disabled_by_default = 9;
string icon = 10;
}
enum LegacyCoverState {
@ -313,6 +315,7 @@ message ListEntitiesFanResponse {
bool supports_direction = 7;
int32 supported_speed_count = 8;
bool disabled_by_default = 9;
string icon = 10;
}
enum FanSpeed {
FAN_SPEED_LOW = 0;
@ -388,6 +391,7 @@ message ListEntitiesLightResponse {
float max_mireds = 10;
repeated string effects = 11;
bool disabled_by_default = 13;
string icon = 14;
}
message LightStateResponse {
option (id) = 24;
@ -790,6 +794,7 @@ message ListEntitiesClimateResponse {
repeated ClimatePreset supported_presets = 16;
repeated string supported_custom_presets = 17;
bool disabled_by_default = 18;
string icon = 19;
}
message ClimateStateResponse {
option (id) = 47;

View file

@ -1,4 +1,5 @@
#include "api_connection.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/log.h"
#include "esphome/components/network/util.h"
#include "esphome/core/version.h"
@ -143,8 +144,8 @@ void APIConnection::loop() {
}
}
std::string get_default_unique_id(const std::string &component_type, Nameable *nameable) {
return App.get_name() + component_type + nameable->get_object_id();
std::string get_default_unique_id(const std::string &component_type, EntityBase *entity) {
return App.get_name() + component_type + entity->get_object_id();
}
DisconnectResponse APIConnection::disconnect(const DisconnectRequest &msg) {
@ -180,6 +181,7 @@ bool APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_
msg.device_class = binary_sensor->get_device_class();
msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor();
msg.disabled_by_default = binary_sensor->is_disabled_by_default();
msg.icon = binary_sensor->get_icon();
return this->send_list_entities_binary_sensor_response(msg);
}
#endif
@ -212,6 +214,7 @@ bool APIConnection::send_cover_info(cover::Cover *cover) {
msg.supports_tilt = traits.get_supports_tilt();
msg.device_class = cover->get_device_class();
msg.disabled_by_default = cover->is_disabled_by_default();
msg.icon = cover->get_icon();
return this->send_list_entities_cover_response(msg);
}
void APIConnection::cover_command(const CoverCommandRequest &msg) {
@ -277,6 +280,7 @@ bool APIConnection::send_fan_info(fan::FanState *fan) {
msg.supports_direction = traits.supports_direction();
msg.supported_speed_count = traits.supported_speed_count();
msg.disabled_by_default = fan->is_disabled_by_default();
msg.icon = fan->get_icon();
return this->send_list_entities_fan_response(msg);
}
void APIConnection::fan_command(const FanCommandRequest &msg) {
@ -339,6 +343,7 @@ bool APIConnection::send_light_info(light::LightState *light) {
msg.unique_id = get_default_unique_id("light", light);
msg.disabled_by_default = light->is_disabled_by_default();
msg.icon = light->get_icon();
for (auto mode : traits.get_supported_color_modes())
msg.supported_color_modes.push_back(static_cast<enums::ColorMode>(mode));
@ -529,6 +534,7 @@ bool APIConnection::send_climate_info(climate::Climate *climate) {
msg.unique_id = get_default_unique_id("climate", climate);
msg.disabled_by_default = climate->is_disabled_by_default();
msg.icon = climate->get_icon();
msg.supports_current_temperature = traits.get_supports_current_temperature();
msg.supports_two_point_target_temperature = traits.get_supports_two_point_target_temperature();
@ -601,7 +607,7 @@ bool APIConnection::send_number_info(number::Number *number) {
msg.object_id = number->get_object_id();
msg.name = number->get_name();
msg.unique_id = get_default_unique_id("number", number);
msg.icon = number->traits.get_icon();
msg.icon = number->get_icon();
msg.disabled_by_default = number->is_disabled_by_default();
msg.min_value = number->traits.get_min_value();
@ -638,7 +644,7 @@ bool APIConnection::send_select_info(select::Select *select) {
msg.object_id = select->get_object_id();
msg.name = select->get_name();
msg.unique_id = get_default_unique_id("select", select);
msg.icon = select->traits.get_icon();
msg.icon = select->get_icon();
msg.disabled_by_default = select->is_disabled_by_default();
for (const auto &option : select->traits.get_options())

View file

@ -2,7 +2,6 @@
// See scripts/api_protobuf/api_protobuf.py
#include "api_pb2.h"
#include "esphome/core/log.h"
#include <cstdio>
namespace esphome {
namespace api {
@ -532,6 +531,10 @@ bool ListEntitiesBinarySensorResponse::decode_length(uint32_t field_id, ProtoLen
this->device_class = value.as_string();
return true;
}
case 8: {
this->icon = value.as_string();
return true;
}
default:
return false;
}
@ -554,6 +557,7 @@ void ListEntitiesBinarySensorResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(5, this->device_class);
buffer.encode_bool(6, this->is_status_binary_sensor);
buffer.encode_bool(7, this->disabled_by_default);
buffer.encode_string(8, this->icon);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const {
@ -587,6 +591,10 @@ void ListEntitiesBinarySensorResponse::dump_to(std::string &out) const {
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append(" icon: ");
out.append("'").append(this->icon).append("'");
out.append("\n");
out.append("}");
}
#endif
@ -678,6 +686,10 @@ bool ListEntitiesCoverResponse::decode_length(uint32_t field_id, ProtoLengthDeli
this->device_class = value.as_string();
return true;
}
case 10: {
this->icon = value.as_string();
return true;
}
default:
return false;
}
@ -702,6 +714,7 @@ void ListEntitiesCoverResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(7, this->supports_tilt);
buffer.encode_string(8, this->device_class);
buffer.encode_bool(9, this->disabled_by_default);
buffer.encode_string(10, this->icon);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesCoverResponse::dump_to(std::string &out) const {
@ -743,6 +756,10 @@ void ListEntitiesCoverResponse::dump_to(std::string &out) const {
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append(" icon: ");
out.append("'").append(this->icon).append("'");
out.append("\n");
out.append("}");
}
#endif
@ -949,6 +966,10 @@ bool ListEntitiesFanResponse::decode_length(uint32_t field_id, ProtoLengthDelimi
this->unique_id = value.as_string();
return true;
}
case 10: {
this->icon = value.as_string();
return true;
}
default:
return false;
}
@ -973,6 +994,7 @@ void ListEntitiesFanResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_bool(7, this->supports_direction);
buffer.encode_int32(8, this->supported_speed_count);
buffer.encode_bool(9, this->disabled_by_default);
buffer.encode_string(10, this->icon);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesFanResponse::dump_to(std::string &out) const {
@ -1015,6 +1037,10 @@ void ListEntitiesFanResponse::dump_to(std::string &out) const {
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append(" icon: ");
out.append("'").append(this->icon).append("'");
out.append("\n");
out.append("}");
}
#endif
@ -1263,6 +1289,10 @@ bool ListEntitiesLightResponse::decode_length(uint32_t field_id, ProtoLengthDeli
this->effects.push_back(value.as_string());
return true;
}
case 14: {
this->icon = value.as_string();
return true;
}
default:
return false;
}
@ -1303,6 +1333,7 @@ void ListEntitiesLightResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(11, it, true);
}
buffer.encode_bool(13, this->disabled_by_default);
buffer.encode_string(14, this->icon);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesLightResponse::dump_to(std::string &out) const {
@ -1366,6 +1397,10 @@ void ListEntitiesLightResponse::dump_to(std::string &out) const {
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append(" icon: ");
out.append("'").append(this->icon).append("'");
out.append("\n");
out.append("}");
}
#endif
@ -3073,6 +3108,10 @@ bool ListEntitiesClimateResponse::decode_length(uint32_t field_id, ProtoLengthDe
this->supported_custom_presets.push_back(value.as_string());
return true;
}
case 19: {
this->icon = value.as_string();
return true;
}
default:
return false;
}
@ -3130,6 +3169,7 @@ void ListEntitiesClimateResponse::encode(ProtoWriteBuffer buffer) const {
buffer.encode_string(17, it, true);
}
buffer.encode_bool(18, this->disabled_by_default);
buffer.encode_string(19, this->icon);
}
#ifdef HAS_PROTO_MESSAGE_DUMP
void ListEntitiesClimateResponse::dump_to(std::string &out) const {
@ -3222,6 +3262,10 @@ void ListEntitiesClimateResponse::dump_to(std::string &out) const {
out.append(" disabled_by_default: ");
out.append(YESNO(this->disabled_by_default));
out.append("\n");
out.append(" icon: ");
out.append("'").append(this->icon).append("'");
out.append("\n");
out.append("}");
}
#endif

View file

@ -269,6 +269,7 @@ class ListEntitiesBinarySensorResponse : public ProtoMessage {
std::string device_class{};
bool is_status_binary_sensor{false};
bool disabled_by_default{false};
std::string icon{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
@ -304,6 +305,7 @@ class ListEntitiesCoverResponse : public ProtoMessage {
bool supports_tilt{false};
std::string device_class{};
bool disabled_by_default{false};
std::string icon{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
@ -360,6 +362,7 @@ class ListEntitiesFanResponse : public ProtoMessage {
bool supports_direction{false};
int32_t supported_speed_count{0};
bool disabled_by_default{false};
std::string icon{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
@ -424,6 +427,7 @@ class ListEntitiesLightResponse : public ProtoMessage {
float max_mireds{0.0f};
std::vector<std::string> effects{};
bool disabled_by_default{false};
std::string icon{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;
@ -856,6 +860,7 @@ class ListEntitiesClimateResponse : public ProtoMessage {
std::vector<enums::ClimatePreset> supported_presets{};
std::vector<std::string> supported_custom_presets{};
bool disabled_by_default{false};
std::string icon{};
void encode(ProtoWriteBuffer buffer) const override;
#ifdef HAS_PROTO_MESSAGE_DUMP
void dump_to(std::string &out) const override;

View file

@ -1,15 +1,14 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.cpp_helpers import setup_entity
from esphome import automation, core
from esphome.automation import Condition, maybe_simple_id
from esphome.components import mqtt
from esphome.const import (
CONF_DELAY,
CONF_DEVICE_CLASS,
CONF_DISABLED_BY_DEFAULT,
CONF_FILTERS,
CONF_ID,
CONF_INTERNAL,
CONF_INVALID_COOLDOWN,
CONF_INVERTED,
CONF_MAX_LENGTH,
@ -88,7 +87,7 @@ DEVICE_CLASSES = [
IS_PLATFORM_COMPONENT = True
binary_sensor_ns = cg.esphome_ns.namespace("binary_sensor")
BinarySensor = binary_sensor_ns.class_("BinarySensor", cg.Nameable)
BinarySensor = binary_sensor_ns.class_("BinarySensor", cg.EntityBase)
BinarySensorInitiallyOff = binary_sensor_ns.class_(
"BinarySensorInitiallyOff", BinarySensor
)
@ -314,7 +313,7 @@ def validate_multi_click_timing(value):
device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
BINARY_SENSOR_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
BINARY_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
{
cv.GenerateID(): cv.declare_id(BinarySensor),
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(
@ -375,10 +374,8 @@ BINARY_SENSOR_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).exten
async def setup_binary_sensor_core_(var, config):
cg.add(var.set_name(config[CONF_NAME]))
cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
if CONF_INTERNAL in config:
cg.add(var.set_internal(config[CONF_INTERNAL]))
await setup_entity(var, config)
if CONF_DEVICE_CLASS in config:
cg.add(var.set_device_class(config[CONF_DEVICE_CLASS]))
if CONF_INVERTED in config:

View file

@ -42,7 +42,7 @@ void BinarySensor::send_state_internal(bool state, bool is_initial) {
}
}
std::string BinarySensor::device_class() { return ""; }
BinarySensor::BinarySensor(const std::string &name) : Nameable(name), state(false) {}
BinarySensor::BinarySensor(const std::string &name) : EntityBase(name), state(false) {}
BinarySensor::BinarySensor() : BinarySensor("") {}
void BinarySensor::set_device_class(const std::string &device_class) { this->device_class_ = device_class; }
std::string BinarySensor::get_device_class() {

View file

@ -1,6 +1,7 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/helpers.h"
#include "esphome/components/binary_sensor/filter.h"
@ -22,7 +23,7 @@ namespace binary_sensor {
* The sub classes should notify the front-end of new states via the publish_state() method which
* handles inverted inputs for you.
*/
class BinarySensor : public Nameable {
class BinarySensor : public EntityBase {
public:
explicit BinarySensor();
/** Construct a binary sensor with the specified name

View file

@ -1,14 +1,13 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.cpp_helpers import setup_entity
from esphome import automation
from esphome.components import mqtt
from esphome.const import (
CONF_AWAY,
CONF_CUSTOM_FAN_MODE,
CONF_CUSTOM_PRESET,
CONF_DISABLED_BY_DEFAULT,
CONF_ID,
CONF_INTERNAL,
CONF_MAX_TEMPERATURE,
CONF_MIN_TEMPERATURE,
CONF_MODE,
@ -19,7 +18,6 @@ from esphome.const import (
CONF_TEMPERATURE_STEP,
CONF_VISUAL,
CONF_MQTT_ID,
CONF_NAME,
CONF_FAN_MODE,
CONF_SWING_MODE,
)
@ -30,7 +28,7 @@ IS_PLATFORM_COMPONENT = True
CODEOWNERS = ["@esphome/core"]
climate_ns = cg.esphome_ns.namespace("climate")
Climate = climate_ns.class_("Climate", cg.Nameable)
Climate = climate_ns.class_("Climate", cg.EntityBase)
ClimateCall = climate_ns.class_("ClimateCall")
ClimateTraits = climate_ns.class_("ClimateTraits")
@ -88,7 +86,7 @@ validate_climate_swing_mode = cv.enum(CLIMATE_SWING_MODES, upper=True)
# Actions
ControlAction = climate_ns.class_("ControlAction", automation.Action)
CLIMATE_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
CLIMATE_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
{
cv.GenerateID(): cv.declare_id(Climate),
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTClimateComponent),
@ -105,10 +103,8 @@ CLIMATE_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).ext
async def setup_climate_core_(var, config):
cg.add(var.set_name(config[CONF_NAME]))
cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
if CONF_INTERNAL in config:
cg.add(var.set_internal(config[CONF_INTERNAL]))
await setup_entity(var, config)
visual = config[CONF_VISUAL]
if CONF_MIN_TEMPERATURE in visual:
cg.add(var.set_visual_min_temperature_override(visual[CONF_MIN_TEMPERATURE]))

View file

@ -440,7 +440,7 @@ void Climate::set_visual_max_temperature_override(float visual_max_temperature_o
void Climate::set_visual_temperature_step_override(float visual_temperature_step_override) {
this->visual_temperature_step_override_ = visual_temperature_step_override;
}
Climate::Climate(const std::string &name) : Nameable(name) {}
Climate::Climate(const std::string &name) : EntityBase(name) {}
Climate::Climate() : Climate("") {}
ClimateCall Climate::make_call() { return ClimateCall(this); }

View file

@ -1,6 +1,7 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/helpers.h"
#include "esphome/core/preferences.h"
#include "esphome/core/log.h"
@ -163,7 +164,7 @@ struct ClimateDeviceRestoreState {
* mode etc). These are read-only for the user and rw for integrations. The reason these are public
* is for simple access to them from lambdas `if (id(my_climate).mode == climate::CLIMATE_MODE_HEAT_COOL) ...`
*/
class Climate : public Nameable {
class Climate : public EntityBase {
public:
/// Construct a climate device with empty name (will be set later).
Climate();

View file

@ -4,18 +4,16 @@ from esphome import automation
from esphome.automation import maybe_simple_id, Condition
from esphome.components import mqtt
from esphome.const import (
CONF_DISABLED_BY_DEFAULT,
CONF_ID,
CONF_INTERNAL,
CONF_DEVICE_CLASS,
CONF_STATE,
CONF_POSITION,
CONF_TILT,
CONF_STOP,
CONF_MQTT_ID,
CONF_NAME,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_helpers import setup_entity
IS_PLATFORM_COMPONENT = True
@ -36,7 +34,7 @@ DEVICE_CLASSES = [
cover_ns = cg.esphome_ns.namespace("cover")
Cover = cover_ns.class_("Cover", cg.Nameable)
Cover = cover_ns.class_("Cover", cg.EntityBase)
COVER_OPEN = cover_ns.COVER_OPEN
COVER_CLOSED = cover_ns.COVER_CLOSED
@ -65,7 +63,7 @@ CoverPublishAction = cover_ns.class_("CoverPublishAction", automation.Action)
CoverIsOpenCondition = cover_ns.class_("CoverIsOpenCondition", Condition)
CoverIsClosedCondition = cover_ns.class_("CoverIsClosedCondition", Condition)
COVER_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
COVER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
{
cv.GenerateID(): cv.declare_id(Cover),
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTCoverComponent),
@ -76,10 +74,8 @@ COVER_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).exten
async def setup_cover_core_(var, config):
cg.add(var.set_name(config[CONF_NAME]))
cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
if CONF_INTERNAL in config:
cg.add(var.set_internal(config[CONF_INTERNAL]))
await setup_entity(var, config)
if CONF_DEVICE_CLASS in config:
cg.add(var.set_device_class(config[CONF_DEVICE_CLASS]))

View file

@ -31,7 +31,7 @@ const char *cover_operation_to_str(CoverOperation op) {
}
}
Cover::Cover(const std::string &name) : Nameable(name), position{COVER_OPEN} {}
Cover::Cover(const std::string &name) : EntityBase(name), position{COVER_OPEN} {}
uint32_t Cover::hash_base() { return 1727367479UL; }

View file

@ -1,6 +1,7 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/helpers.h"
#include "esphome/core/preferences.h"
#include "cover_traits.h"
@ -107,7 +108,7 @@ const char *cover_operation_to_str(CoverOperation op);
* to control all values of the cover. Also implement get_traits() to return what operations
* the cover supports.
*/
class Cover : public Nameable {
class Cover : public EntityBase {
public:
explicit Cover();
explicit Cover(const std::string &name);

View file

@ -21,7 +21,7 @@ from esphome.components.esp32 import add_idf_sdkconfig_option
DEPENDENCIES = ["esp32", "api"]
esp32_camera_ns = cg.esphome_ns.namespace("esp32_camera")
ESP32Camera = esp32_camera_ns.class_("ESP32Camera", cg.PollingComponent, cg.Nameable)
ESP32Camera = esp32_camera_ns.class_("ESP32Camera", cg.PollingComponent, cg.EntityBase)
ESP32CameraFrameSize = esp32_camera_ns.enum("ESP32CameraFrameSize")
FRAME_SIZES = {
"160X120": ESP32CameraFrameSize.ESP32_CAMERA_SIZE_160X120,

View file

@ -172,7 +172,7 @@ void ESP32Camera::framebuffer_task(void *pv) {
esp_camera_fb_return(framebuffer);
}
}
ESP32Camera::ESP32Camera(const std::string &name) : Nameable(name) {
ESP32Camera::ESP32Camera(const std::string &name) : EntityBase(name) {
this->config_.pin_pwdn = -1;
this->config_.pin_reset = -1;
this->config_.pin_xclk = -1;

View file

@ -3,6 +3,7 @@
#ifdef USE_ESP32
#include "esphome/core/component.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/helpers.h"
#include <esp_camera.h>
#include <freertos/FreeRTOS.h>
@ -50,7 +51,7 @@ enum ESP32CameraFrameSize {
ESP32_CAMERA_SIZE_1600X1200, // UXGA
};
class ESP32Camera : public Component, public Nameable {
class ESP32Camera : public Component, public EntityBase {
public:
ESP32Camera(const std::string &name);
void set_data_pins(std::array<uint8_t, 8> pins);

View file

@ -2,7 +2,6 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import binary_sensor
from esphome.const import (
CONF_NAME,
CONF_PIN,
CONF_THRESHOLD,
CONF_ID,
@ -55,7 +54,6 @@ async def to_code(config):
hub = await cg.get_variable(config[CONF_ESP32_TOUCH_ID])
var = cg.new_Pvariable(
config[CONF_ID],
config[CONF_NAME],
TOUCH_PADS[config[CONF_PIN]],
config[CONF_THRESHOLD],
config[CONF_WAKEUP_THRESHOLD],

View file

@ -159,9 +159,8 @@ void ESP32TouchComponent::on_shutdown() {
}
}
ESP32TouchBinarySensor::ESP32TouchBinarySensor(const std::string &name, touch_pad_t touch_pad, uint16_t threshold,
uint16_t wakeup_threshold)
: BinarySensor(name), touch_pad_(touch_pad), threshold_(threshold), wakeup_threshold_(wakeup_threshold) {}
ESP32TouchBinarySensor::ESP32TouchBinarySensor(touch_pad_t touch_pad, uint16_t threshold, uint16_t wakeup_threshold)
: BinarySensor(), touch_pad_(touch_pad), threshold_(threshold), wakeup_threshold_(wakeup_threshold) {}
} // namespace esp32_touch
} // namespace esphome

View file

@ -64,7 +64,7 @@ class ESP32TouchComponent : public Component {
/// Simple helper class to expose a touch pad value as a binary sensor.
class ESP32TouchBinarySensor : public binary_sensor::BinarySensor {
public:
ESP32TouchBinarySensor(const std::string &name, touch_pad_t touch_pad, uint16_t threshold, uint16_t wakeup_threshold);
ESP32TouchBinarySensor(touch_pad_t touch_pad, uint16_t threshold, uint16_t wakeup_threshold);
touch_pad_t get_touch_pad() const { return touch_pad_; }
uint16_t get_threshold() const { return threshold_; }

View file

@ -4,9 +4,7 @@ from esphome import automation
from esphome.automation import maybe_simple_id
from esphome.components import mqtt
from esphome.const import (
CONF_DISABLED_BY_DEFAULT,
CONF_ID,
CONF_INTERNAL,
CONF_MQTT_ID,
CONF_OSCILLATING,
CONF_OSCILLATION_COMMAND_TOPIC,
@ -16,7 +14,6 @@ from esphome.const import (
CONF_SPEED_LEVEL_STATE_TOPIC,
CONF_SPEED_COMMAND_TOPIC,
CONF_SPEED_STATE_TOPIC,
CONF_NAME,
CONF_ON_SPEED_SET,
CONF_ON_TURN_OFF,
CONF_ON_TURN_ON,
@ -24,11 +21,12 @@ from esphome.const import (
CONF_DIRECTION,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_helpers import setup_entity
IS_PLATFORM_COMPONENT = True
fan_ns = cg.esphome_ns.namespace("fan")
FanState = fan_ns.class_("FanState", cg.Nameable, cg.Component)
FanState = fan_ns.class_("FanState", cg.EntityBase, cg.Component)
MakeFan = cg.Application.struct("MakeFan")
FanDirection = fan_ns.enum("FanDirection")
@ -50,7 +48,7 @@ FanSpeedSetTrigger = fan_ns.class_("FanSpeedSetTrigger", automation.Trigger.temp
FanIsOnCondition = fan_ns.class_("FanIsOnCondition", automation.Condition.template())
FanIsOffCondition = fan_ns.class_("FanIsOffCondition", automation.Condition.template())
FAN_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
FAN_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
{
cv.GenerateID(): cv.declare_id(FanState),
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTFanComponent),
@ -92,10 +90,7 @@ FAN_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
async def setup_fan_core_(var, config):
cg.add(var.set_name(config[CONF_NAME]))
cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
if CONF_INTERNAL in config:
cg.add(var.set_internal(config[CONF_INTERNAL]))
await setup_entity(var, config)
if CONF_MQTT_ID in config:
mqtt_ = cg.new_Pvariable(config[CONF_MQTT_ID], var)

View file

@ -12,7 +12,7 @@ void FanState::set_traits(const FanTraits &traits) { this->traits_ = traits; }
void FanState::add_on_state_callback(std::function<void()> &&callback) {
this->state_callback_.add(std::move(callback));
}
FanState::FanState(const std::string &name) : Nameable(name) {}
FanState::FanState(const std::string &name) : EntityBase(name) {}
FanStateCall FanState::turn_on() { return this->make_call().set_state(true); }
FanStateCall FanState::turn_off() { return this->make_call().set_state(false); }

View file

@ -1,6 +1,7 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/helpers.h"
#include "esphome/core/preferences.h"
#include "esphome/core/log.h"
@ -66,7 +67,7 @@ class FanStateCall {
optional<FanDirection> direction_{};
};
class FanState : public Nameable, public Component {
class FanState : public EntityBase, public Component {
public:
FanState() = default;
/// Construct the fan state with name.

View file

@ -64,7 +64,6 @@ class IntegrationSensor : public sensor::Sensor, public Component {
this->rtc_.save(&result_f);
}
std::string unit_of_measurement() override;
std::string icon() override { return this->sensor_->get_icon(); }
int8_t accuracy_decimals() override { return this->sensor_->get_accuracy_decimals() + 2; }
sensor::Sensor *sensor_;

View file

@ -2,7 +2,8 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import automation
from esphome.components import sensor
from esphome.const import CONF_ID, CONF_SENSOR, CONF_RESTORE
from esphome.const import CONF_ICON, CONF_ID, CONF_SENSOR, CONF_RESTORE
from esphome.core.entity_helpers import inherit_property_from
integration_ns = cg.esphome_ns.namespace("integration")
IntegrationSensor = integration_ns.class_(
@ -29,7 +30,6 @@ CONF_TIME_UNIT = "time_unit"
CONF_INTEGRATION_METHOD = "integration_method"
CONF_MIN_SAVE_INTERVAL = "min_save_interval"
CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(IntegrationSensor),
@ -46,6 +46,19 @@ CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend(
).extend(cv.COMPONENT_SCHEMA)
FINAL_VALIDATE_SCHEMA = cv.All(
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(IntegrationSensor),
cv.Optional(CONF_ICON): cv.icon,
cv.Required(CONF_SENSOR): cv.use_id(sensor.Sensor),
},
extra=cv.ALLOW_EXTRA,
),
inherit_property_from(CONF_ICON, CONF_SENSOR),
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])

View file

@ -5,13 +5,10 @@ from esphome.components import mqtt, power_supply
from esphome.const import (
CONF_COLOR_CORRECT,
CONF_DEFAULT_TRANSITION_LENGTH,
CONF_DISABLED_BY_DEFAULT,
CONF_EFFECTS,
CONF_FLASH_TRANSITION_LENGTH,
CONF_GAMMA_CORRECT,
CONF_ID,
CONF_INTERNAL,
CONF_NAME,
CONF_MQTT_ID,
CONF_POWER_SUPPLY,
CONF_RESTORE_MODE,
@ -22,6 +19,7 @@ from esphome.const import (
CONF_WARM_WHITE_COLOR_TEMPERATURE,
)
from esphome.core import coroutine_with_priority
from esphome.cpp_helpers import setup_entity
from .automation import light_control_to_code # noqa
from .effects import (
validate_effects,
@ -54,7 +52,7 @@ RESTORE_MODES = {
"RESTORE_INVERTED_DEFAULT_ON": LightRestoreMode.LIGHT_RESTORE_INVERTED_DEFAULT_ON,
}
LIGHT_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
LIGHT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
{
cv.GenerateID(): cv.declare_id(LightState),
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTJSONLightComponent),
@ -126,10 +124,10 @@ def validate_color_temperature_channels(value):
async def setup_light_core_(light_var, output_var, config):
cg.add(light_var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
await setup_entity(light_var, config)
cg.add(light_var.set_restore_mode(config[CONF_RESTORE_MODE]))
if CONF_INTERNAL in config:
cg.add(light_var.set_internal(config[CONF_INTERNAL]))
if CONF_DEFAULT_TRANSITION_LENGTH in config:
cg.add(
light_var.set_default_transition_length(
@ -167,7 +165,7 @@ async def setup_light_core_(light_var, output_var, config):
async def register_light(output_var, config):
light_var = cg.new_Pvariable(config[CONF_ID], config[CONF_NAME], output_var)
light_var = cg.new_Pvariable(config[CONF_ID], output_var)
cg.add(cg.App.register_light(light_var))
await cg.register_component(light_var, config)
await setup_light_core_(light_var, output_var, config)

View file

@ -8,7 +8,8 @@ namespace light {
static const char *const TAG = "light";
LightState::LightState(const std::string &name, LightOutput *output) : Nameable(name), output_(output) {}
LightState::LightState(const std::string &name, LightOutput *output) : EntityBase(name), output_(output) {}
LightState::LightState(LightOutput *output) : output_(output) {}
LightTraits LightState::get_traits() { return this->output_->get_traits(); }
LightCall LightState::turn_on() { return this->make_call().set_state(true); }

View file

@ -1,6 +1,7 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/optional.h"
#include "esphome/core/preferences.h"
#include "light_call.h"
@ -26,11 +27,13 @@ enum LightRestoreMode {
/** This class represents the communication layer between the front-end MQTT layer and the
* hardware output layer.
*/
class LightState : public Nameable, public Component {
class LightState : public EntityBase, public Component {
public:
/// Construct this LightState using the provided traits and name.
LightState(const std::string &name, LightOutput *output);
LightState(LightOutput *output);
LightTraits get_traits();
/// Make a light state call

View file

@ -3,7 +3,7 @@ from esphome import automation
# Base
light_ns = cg.esphome_ns.namespace("light")
LightState = light_ns.class_("LightState", cg.Nameable, cg.Component)
LightState = light_ns.class_("LightState", cg.EntityBase, cg.Component)
# Fake class for addressable lights
AddressableLightState = light_ns.class_("LightState", LightState)
LightOutput = light_ns.class_("LightOutput")

View file

@ -37,10 +37,8 @@ float MCP3008::read_data(uint8_t pin) {
return data / 1023.0f;
}
MCP3008Sensor::MCP3008Sensor(MCP3008 *parent, const std::string &name, uint8_t pin, float reference_voltage)
: PollingComponent(1000), parent_(parent), pin_(pin), reference_voltage_(reference_voltage) {
this->set_name(name);
}
MCP3008Sensor::MCP3008Sensor(MCP3008 *parent, uint8_t pin, float reference_voltage)
: PollingComponent(1000), parent_(parent), pin_(pin), reference_voltage_(reference_voltage) {}
float MCP3008Sensor::get_setup_priority() const { return setup_priority::DATA; }

View file

@ -26,7 +26,7 @@ class MCP3008 : public Component,
class MCP3008Sensor : public PollingComponent, public sensor::Sensor, public voltage_sampler::VoltageSampler {
public:
MCP3008Sensor(MCP3008 *parent, const std::string &name, uint8_t pin, float reference_voltage);
MCP3008Sensor(MCP3008 *parent, uint8_t pin, float reference_voltage);
void set_reference_voltage(float reference_voltage) { reference_voltage_ = reference_voltage; }
void setup() override;

View file

@ -1,7 +1,7 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, voltage_sampler
from esphome.const import CONF_ID, CONF_NUMBER, CONF_NAME
from esphome.const import CONF_ID, CONF_NUMBER
from . import mcp3008_ns, MCP3008
AUTO_LOAD = ["voltage_sampler"]
@ -29,7 +29,6 @@ async def to_code(config):
var = cg.new_Pvariable(
config[CONF_ID],
parent,
config[CONF_NAME],
config[CONF_NUMBER],
config[CONF_REFERENCE_VOLTAGE],
)

View file

@ -10,6 +10,7 @@ namespace mqtt {
static const char *const TAG = "mqtt.binary_sensor";
std::string MQTTBinarySensorComponent::component_type() const { return "binary_sensor"; }
const EntityBase *MQTTBinarySensorComponent::get_entity() const { return this->binary_sensor_; }
void MQTTBinarySensorComponent::setup() {
this->binary_sensor_->add_on_state_callback([this](bool state) { this->publish_state(state); });
@ -25,7 +26,6 @@ MQTTBinarySensorComponent::MQTTBinarySensorComponent(binary_sensor::BinarySensor
this->set_custom_state_topic(mqtt::global_mqtt_client->get_availability().topic);
}
}
std::string MQTTBinarySensorComponent::friendly_name() const { return this->binary_sensor_->get_name(); }
void MQTTBinarySensorComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) {
if (!this->binary_sensor_->get_device_class().empty())
@ -43,7 +43,6 @@ bool MQTTBinarySensorComponent::send_initial_state() {
return true;
}
}
bool MQTTBinarySensorComponent::is_internal() { return this->binary_sensor_->is_internal(); }
bool MQTTBinarySensorComponent::publish_state(bool state) {
if (this->binary_sensor_->is_status_binary_sensor())
return true;

View file

@ -28,11 +28,10 @@ class MQTTBinarySensorComponent : public mqtt::MQTTComponent {
bool send_initial_state() override;
bool publish_state(bool state);
bool is_internal() override;
protected:
std::string friendly_name() const override;
std::string component_type() const override;
const EntityBase *get_entity() const override;
binary_sensor::BinarySensor *binary_sensor_;
};

View file

@ -212,9 +212,9 @@ void MQTTClimateComponent::setup() {
}
MQTTClimateComponent::MQTTClimateComponent(Climate *device) : device_(device) {}
bool MQTTClimateComponent::send_initial_state() { return this->publish_state_(); }
bool MQTTClimateComponent::is_internal() { return this->device_->is_internal(); }
std::string MQTTClimateComponent::component_type() const { return "climate"; }
std::string MQTTClimateComponent::friendly_name() const { return this->device_->get_name(); }
const EntityBase *MQTTClimateComponent::get_entity() const { return this->device_; }
bool MQTTClimateComponent::publish_state_() {
auto traits = this->device_->get_traits();
// mode

View file

@ -16,7 +16,6 @@ class MQTTClimateComponent : public mqtt::MQTTComponent {
MQTTClimateComponent(climate::Climate *device);
void send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) override;
bool send_initial_state() override;
bool is_internal() override;
std::string component_type() const override;
void setup() override;
@ -38,7 +37,7 @@ class MQTTClimateComponent : public mqtt::MQTTComponent {
MQTT_COMPONENT_CUSTOM_TOPIC(swing_mode, command)
protected:
std::string friendly_name() const override;
const EntityBase *get_entity() const override;
bool publish_state_();

View file

@ -68,8 +68,13 @@ bool MQTTComponent::send_discovery_() {
this->send_discovery(root, config);
std::string name = this->friendly_name();
root["name"] = name;
// Fields from EntityBase
root["name"] = this->friendly_name();
if (this->is_disabled_by_default())
root["enabled_by_default"] = false;
if (!this->get_icon().empty())
root["icon"] = this->get_icon();
if (config.state_topic)
root["state_topic"] = this->get_state_topic_();
if (config.command_topic)
@ -199,6 +204,12 @@ void MQTTComponent::schedule_resend_state() { this->resend_state_ = true; }
std::string MQTTComponent::unique_id() { return ""; }
bool MQTTComponent::is_connected_() const { return global_mqtt_client->is_connected(); }
// Pull these properties from EntityBase if not overridden
std::string MQTTComponent::friendly_name() const { return this->get_entity()->get_name(); }
std::string MQTTComponent::get_icon() const { return this->get_entity()->get_icon(); }
bool MQTTComponent::is_disabled_by_default() const { return this->get_entity()->is_disabled_by_default(); }
bool MQTTComponent::is_internal() { return this->get_entity()->is_internal(); }
} // namespace mqtt
} // namespace esphome

View file

@ -7,6 +7,7 @@
#include <memory>
#include "esphome/core/component.h"
#include "esphome/core/entity_base.h"
#include "mqtt_client.h"
namespace esphome {
@ -73,7 +74,7 @@ class MQTTComponent : public Component {
virtual bool send_initial_state() = 0;
virtual bool is_internal() = 0;
virtual bool is_internal();
/// Set whether state message should be retained.
void set_retain(bool retain);
@ -148,8 +149,10 @@ class MQTTComponent : public Component {
*/
std::string get_default_topic_for_(const std::string &suffix) const;
/// Get the friendly name of this MQTT component.
virtual std::string friendly_name() const = 0;
/**
* Gets the Entity served by this MQTT component.
*/
virtual const EntityBase *get_entity() const = 0;
/** A unique ID for this MQTT component, empty for no unique id. See unique ID requirements:
* https://developers.home-assistant.io/docs/en/entity_registry_index.html#unique-id-requirements
@ -158,6 +161,15 @@ class MQTTComponent : public Component {
*/
virtual std::string unique_id();
/// Get the friendly name of this MQTT component.
virtual std::string friendly_name() const;
/// Get the icon field of this component
virtual std::string get_icon() const;
/// Get whether the underlying Entity is disabled by default
virtual bool is_disabled_by_default() const;
/// Get the MQTT topic that new states will be shared to.
const std::string get_state_topic_() const;

View file

@ -84,9 +84,9 @@ void MQTTCoverComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryCon
}
std::string MQTTCoverComponent::component_type() const { return "cover"; }
std::string MQTTCoverComponent::friendly_name() const { return this->cover_->get_name(); }
const EntityBase *MQTTCoverComponent::get_entity() const { return this->cover_; }
bool MQTTCoverComponent::send_initial_state() { return this->publish_state(); }
bool MQTTCoverComponent::is_internal() { return this->cover_->is_internal(); }
bool MQTTCoverComponent::publish_state() {
auto traits = this->cover_->get_traits();
bool success = true;

View file

@ -24,7 +24,6 @@ class MQTTCoverComponent : public mqtt::MQTTComponent {
MQTT_COMPONENT_CUSTOM_TOPIC(tilt, state)
bool send_initial_state() override;
bool is_internal() override;
bool publish_state();
@ -32,7 +31,7 @@ class MQTTCoverComponent : public mqtt::MQTTComponent {
protected:
std::string component_type() const override;
std::string friendly_name() const override;
const EntityBase *get_entity() const override;
cover::Cover *cover_;
};

View file

@ -16,6 +16,7 @@ MQTTFanComponent::MQTTFanComponent(FanState *state) : MQTTComponent(), state_(st
FanState *MQTTFanComponent::get_state() const { return this->state_; }
std::string MQTTFanComponent::component_type() const { return "fan"; }
const EntityBase *MQTTFanComponent::get_entity() const { return this->state_; }
void MQTTFanComponent::setup() {
this->subscribe(this->get_command_topic_(), [this](const std::string &topic, const std::string &payload) {
@ -113,7 +114,7 @@ void MQTTFanComponent::dump_config() {
}
bool MQTTFanComponent::send_initial_state() { return this->publish_state(); }
std::string MQTTFanComponent::friendly_name() const { return this->state_->get_name(); }
void MQTTFanComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) {
if (this->state_->get_traits().supports_oscillation()) {
root["oscillation_command_topic"] = this->get_oscillation_command_topic();
@ -126,7 +127,6 @@ void MQTTFanComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfi
root["speed_state_topic"] = this->get_speed_state_topic();
}
}
bool MQTTFanComponent::is_internal() { return this->state_->is_internal(); }
bool MQTTFanComponent::publish_state() {
const char *state_s = this->state_->state ? "ON" : "OFF";
ESP_LOGD(TAG, "'%s' Sending state %s.", this->state_->get_name().c_str(), state_s);

View file

@ -39,10 +39,8 @@ class MQTTFanComponent : public mqtt::MQTTComponent {
fan::FanState *get_state() const;
bool is_internal() override;
protected:
std::string friendly_name() const override;
const EntityBase *get_entity() const override;
fan::FanState *state_;
};

View file

@ -13,6 +13,7 @@ static const char *const TAG = "mqtt.light";
using namespace esphome::light;
std::string MQTTJSONLightComponent::component_type() const { return "light"; }
const EntityBase *MQTTJSONLightComponent::get_entity() const { return this->state_; }
void MQTTJSONLightComponent::setup() {
this->subscribe_json(this->get_command_topic_(), [this](const std::string &topic, JsonObject &root) {
@ -32,7 +33,7 @@ bool MQTTJSONLightComponent::publish_state_() {
[this](JsonObject &root) { LightJSONSchema::dump_json(*this->state_, root); });
}
LightState *MQTTJSONLightComponent::get_state() const { return this->state_; }
std::string MQTTJSONLightComponent::friendly_name() const { return this->state_->get_name(); }
void MQTTJSONLightComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) {
root["schema"] = "json";
auto traits = this->state_->get_traits();
@ -70,7 +71,6 @@ void MQTTJSONLightComponent::send_discovery(JsonObject &root, mqtt::SendDiscover
}
}
bool MQTTJSONLightComponent::send_initial_state() { return this->publish_state_(); }
bool MQTTJSONLightComponent::is_internal() { return this->state_->is_internal(); }
void MQTTJSONLightComponent::dump_config() {
ESP_LOGCONFIG(TAG, "MQTT Light '%s':", this->state_->get_name().c_str());
LOG_MQTT_COMPONENT(true, true)

View file

@ -25,11 +25,9 @@ class MQTTJSONLightComponent : public mqtt::MQTTComponent {
bool send_initial_state() override;
bool is_internal() override;
protected:
std::string friendly_name() const override;
std::string component_type() const override;
const EntityBase *get_entity() const override;
bool publish_state_();

View file

@ -33,13 +33,11 @@ void MQTTNumberComponent::dump_config() {
}
std::string MQTTNumberComponent::component_type() const { return "number"; }
const EntityBase *MQTTNumberComponent::get_entity() const { return this->number_; }
std::string MQTTNumberComponent::friendly_name() const { return this->number_->get_name(); }
void MQTTNumberComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) {
const auto &traits = number_->traits;
// https://www.home-assistant.io/integrations/number.mqtt/
if (!traits.get_icon().empty())
root["icon"] = traits.get_icon();
root["min"] = traits.get_min_value();
root["max"] = traits.get_max_value();
root["step"] = traits.get_step();
@ -53,7 +51,6 @@ bool MQTTNumberComponent::send_initial_state() {
return true;
}
}
bool MQTTNumberComponent::is_internal() { return this->number_->is_internal(); }
bool MQTTNumberComponent::publish_state(float value) {
char buffer[64];
snprintf(buffer, sizeof(buffer), "%f", value);

View file

@ -28,15 +28,13 @@ class MQTTNumberComponent : public mqtt::MQTTComponent {
void send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) override;
bool send_initial_state() override;
bool is_internal() override;
bool publish_state(float value);
protected:
/// Override for MQTTComponent, returns "number".
std::string component_type() const override;
std::string friendly_name() const override;
const EntityBase *get_entity() const override;
number::Number *number_;
};

View file

@ -28,13 +28,11 @@ void MQTTSelectComponent::dump_config() {
}
std::string MQTTSelectComponent::component_type() const { return "select"; }
const EntityBase *MQTTSelectComponent::get_entity() const { return this->select_; }
std::string MQTTSelectComponent::friendly_name() const { return this->select_->get_name(); }
void MQTTSelectComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) {
const auto &traits = select_->traits;
// https://www.home-assistant.io/integrations/select.mqtt/
if (!traits.get_icon().empty())
root["icon"] = traits.get_icon();
JsonArray &options = root.createNestedArray("options");
for (const auto &option : traits.get_options())
options.add(option);
@ -48,7 +46,6 @@ bool MQTTSelectComponent::send_initial_state() {
return true;
}
}
bool MQTTSelectComponent::is_internal() { return this->select_->is_internal(); }
bool MQTTSelectComponent::publish_state(const std::string &value) {
return this->publish(this->get_state_topic_(), value);
}

View file

@ -28,15 +28,13 @@ class MQTTSelectComponent : public mqtt::MQTTComponent {
void send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) override;
bool send_initial_state() override;
bool is_internal() override;
bool publish_state(const std::string &value);
protected:
/// Override for MQTTComponent, returns "select".
std::string component_type() const override;
std::string friendly_name() const override;
const EntityBase *get_entity() const override;
select::Select *select_;
};

View file

@ -30,6 +30,7 @@ void MQTTSensorComponent::dump_config() {
}
std::string MQTTSensorComponent::component_type() const { return "sensor"; }
const EntityBase *MQTTSensorComponent::get_entity() const { return this->sensor_; }
uint32_t MQTTSensorComponent::get_expire_after() const {
if (this->expire_after_.has_value())
@ -38,7 +39,7 @@ uint32_t MQTTSensorComponent::get_expire_after() const {
}
void MQTTSensorComponent::set_expire_after(uint32_t expire_after) { this->expire_after_ = expire_after; }
void MQTTSensorComponent::disable_expire_after() { this->expire_after_ = 0; }
std::string MQTTSensorComponent::friendly_name() const { return this->sensor_->get_name(); }
void MQTTSensorComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) {
if (!this->sensor_->get_device_class().empty())
root["device_class"] = this->sensor_->get_device_class();
@ -49,9 +50,6 @@ void MQTTSensorComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryCo
if (this->get_expire_after() > 0)
root["expire_after"] = this->get_expire_after() / 1000;
if (!this->sensor_->get_icon().empty())
root["icon"] = this->sensor_->get_icon();
if (this->sensor_->get_force_update())
root["force_update"] = true;
@ -67,7 +65,6 @@ bool MQTTSensorComponent::send_initial_state() {
return true;
}
}
bool MQTTSensorComponent::is_internal() { return this->sensor_->is_internal(); }
bool MQTTSensorComponent::publish_state(float value) {
int8_t accuracy = this->sensor_->get_accuracy_decimals();
return this->publish(this->get_state_topic_(), value_accuracy_to_string(value, accuracy));

View file

@ -41,14 +41,11 @@ class MQTTSensorComponent : public mqtt::MQTTComponent {
bool publish_state(float value);
bool send_initial_state() override;
bool is_internal() override;
protected:
/// Override for MQTTComponent, returns "sensor".
std::string component_type() const override;
std::string friendly_name() const override;
const EntityBase *get_entity() const override;
std::string unique_id() override;
sensor::Sensor *sensor_;

View file

@ -41,15 +41,13 @@ void MQTTSwitchComponent::dump_config() {
}
std::string MQTTSwitchComponent::component_type() const { return "switch"; }
const EntityBase *MQTTSwitchComponent::get_entity() const { return this->switch_; }
void MQTTSwitchComponent::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) {
if (!this->switch_->get_icon().empty())
root["icon"] = this->switch_->get_icon();
if (this->switch_->assumed_state())
root["optimistic"] = true;
}
bool MQTTSwitchComponent::send_initial_state() { return this->publish_state(this->switch_->state); }
bool MQTTSwitchComponent::is_internal() { return this->switch_->is_internal(); }
std::string MQTTSwitchComponent::friendly_name() const { return this->switch_->get_name(); }
bool MQTTSwitchComponent::publish_state(bool state) {
const char *state_s = state ? "ON" : "OFF";
return this->publish(this->get_state_topic_(), state_s);

View file

@ -23,15 +23,13 @@ class MQTTSwitchComponent : public mqtt::MQTTComponent {
void send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) override;
bool send_initial_state() override;
bool is_internal() override;
bool publish_state(bool state);
protected:
std::string friendly_name() const override;
/// "switch" component type.
std::string component_type() const override;
const EntityBase *get_entity() const override;
switch_::Switch *switch_;
};

View file

@ -13,9 +13,6 @@ using namespace esphome::text_sensor;
MQTTTextSensor::MQTTTextSensor(TextSensor *sensor) : MQTTComponent(), sensor_(sensor) {}
void MQTTTextSensor::send_discovery(JsonObject &root, mqtt::SendDiscoveryConfig &config) {
if (!this->sensor_->get_icon().empty())
root["icon"] = this->sensor_->get_icon();
config.command_topic = false;
}
void MQTTTextSensor::setup() {
@ -35,9 +32,8 @@ bool MQTTTextSensor::send_initial_state() {
return true;
}
}
bool MQTTTextSensor::is_internal() { return this->sensor_->is_internal(); }
std::string MQTTTextSensor::component_type() const { return "sensor"; }
std::string MQTTTextSensor::friendly_name() const { return this->sensor_->get_name(); }
const EntityBase *MQTTTextSensor::get_entity() const { return this->sensor_; }
std::string MQTTTextSensor::unique_id() { return this->sensor_->unique_id(); }
} // namespace mqtt

View file

@ -25,13 +25,9 @@ class MQTTTextSensor : public mqtt::MQTTComponent {
bool send_initial_state() override;
bool is_internal() override;
protected:
std::string component_type() const override;
std::string friendly_name() const override;
const EntityBase *get_entity() const override;
std::string unique_id() override;
text_sensor::TextSensor *sensor_;

View file

@ -6,25 +6,21 @@ from esphome.components import mqtt
from esphome.const import (
CONF_ABOVE,
CONF_BELOW,
CONF_DISABLED_BY_DEFAULT,
CONF_ICON,
CONF_ID,
CONF_INTERNAL,
CONF_ON_VALUE,
CONF_ON_VALUE_RANGE,
CONF_TRIGGER_ID,
CONF_NAME,
CONF_MQTT_ID,
CONF_VALUE,
ICON_EMPTY,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@esphome/core"]
IS_PLATFORM_COMPONENT = True
number_ns = cg.esphome_ns.namespace("number")
Number = number_ns.class_("Number", cg.Nameable)
Number = number_ns.class_("Number", cg.EntityBase)
NumberPtr = Number.operator("ptr")
# Triggers
@ -46,11 +42,10 @@ NumberInRangeCondition = number_ns.class_(
icon = cv.icon
NUMBER_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
NUMBER_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
{
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTNumberComponent),
cv.GenerateID(): cv.declare_id(Number),
cv.Optional(CONF_ICON, default=ICON_EMPTY): icon,
cv.Optional(CONF_ON_VALUE): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(NumberStateTrigger),
@ -71,12 +66,8 @@ NUMBER_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
async def setup_number_core_(
var, config, *, min_value: float, max_value: float, step: Optional[float]
):
cg.add(var.set_name(config[CONF_NAME]))
cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
if CONF_INTERNAL in config:
cg.add(var.set_internal(config[CONF_INTERNAL]))
await setup_entity(var, config)
cg.add(var.traits.set_icon(config[CONF_ICON]))
cg.add(var.traits.set_min_value(min_value))
cg.add(var.traits.set_max_value(max_value))
if step is not None:

View file

@ -1,6 +1,7 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/helpers.h"
namespace esphome {
@ -9,8 +10,8 @@ namespace number {
#define LOG_NUMBER(prefix, type, obj) \
if ((obj) != nullptr) { \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
if (!(obj)->traits.get_icon().empty()) { \
ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->traits.get_icon().c_str()); \
if (!(obj)->get_icon().empty()) { \
ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon().c_str()); \
} \
}
@ -40,21 +41,18 @@ class NumberTraits {
float get_max_value() const { return max_value_; }
void set_step(float step) { step_ = step; }
float get_step() const { return step_; }
void set_icon(std::string icon) { icon_ = std::move(icon); }
const std::string &get_icon() const { return icon_; }
protected:
float min_value_ = NAN;
float max_value_ = NAN;
float step_ = NAN;
std::string icon_;
};
/** Base-class for all numbers.
*
* A number can use publish_state to send out a new value.
*/
class Number : public Nameable {
class Number : public EntityBase {
public:
float state;

View file

@ -1,6 +1,4 @@
import esphome.codegen as cg
from esphome.components import binary_sensor, remote_base
from esphome.const import CONF_NAME
DEPENDENCIES = ["remote_receiver"]
@ -9,5 +7,4 @@ CONFIG_SCHEMA = remote_base.validate_binary_sensor
async def to_code(config):
var = await remote_base.build_binary_sensor(config)
cg.add(var.set_name(config[CONF_NAME]))
await binary_sensor.register_binary_sensor(var, config)

View file

@ -4,24 +4,20 @@ import esphome.config_validation as cv
from esphome import automation
from esphome.components import mqtt
from esphome.const import (
CONF_DISABLED_BY_DEFAULT,
CONF_ICON,
CONF_ID,
CONF_INTERNAL,
CONF_ON_VALUE,
CONF_OPTION,
CONF_TRIGGER_ID,
CONF_NAME,
CONF_MQTT_ID,
ICON_EMPTY,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@esphome/core"]
IS_PLATFORM_COMPONENT = True
select_ns = cg.esphome_ns.namespace("select")
Select = select_ns.class_("Select", cg.Nameable)
Select = select_ns.class_("Select", cg.EntityBase)
SelectPtr = Select.operator("ptr")
# Triggers
@ -35,11 +31,10 @@ SelectSetAction = select_ns.class_("SelectSetAction", automation.Action)
icon = cv.icon
SELECT_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
SELECT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
{
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTSelectComponent),
cv.GenerateID(): cv.declare_id(Select),
cv.Optional(CONF_ICON, default=ICON_EMPTY): icon,
cv.Optional(CONF_ON_VALUE): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(SelectStateTrigger),
@ -50,12 +45,8 @@ SELECT_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
async def setup_select_core_(var, config, *, options: List[str]):
cg.add(var.set_name(config[CONF_NAME]))
cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
if CONF_INTERNAL in config:
cg.add(var.set_internal(config[CONF_INTERNAL]))
await setup_entity(var, config)
cg.add(var.traits.set_icon(config[CONF_ICON]))
cg.add(var.traits.set_options(options))
for conf in config.get(CONF_ON_VALUE, []):

View file

@ -3,6 +3,7 @@
#include <set>
#include <utility>
#include "esphome/core/component.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/helpers.h"
namespace esphome {
@ -11,8 +12,8 @@ namespace select {
#define LOG_SELECT(prefix, type, obj) \
if ((obj) != nullptr) { \
ESP_LOGCONFIG(TAG, "%s%s '%s'", prefix, LOG_STR_LITERAL(type), (obj)->get_name().c_str()); \
if (!(obj)->traits.get_icon().empty()) { \
ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->traits.get_icon().c_str()); \
if (!(obj)->get_icon().empty()) { \
ESP_LOGCONFIG(TAG, "%s Icon: '%s'", prefix, (obj)->get_icon().c_str()); \
} \
}
@ -38,19 +39,16 @@ class SelectTraits {
public:
void set_options(std::vector<std::string> options) { this->options_ = std::move(options); }
const std::vector<std::string> get_options() const { return this->options_; }
void set_icon(std::string icon) { icon_ = std::move(icon); }
const std::string &get_icon() const { return icon_; }
protected:
std::vector<std::string> options_;
std::string icon_;
};
/** Base-class for all selects.
*
* A select can use publish_state to send out a new value.
*/
class Select : public Nameable {
class Select : public EntityBase {
public:
std::string state;

View file

@ -10,13 +10,11 @@ from esphome.const import (
CONF_ACCURACY_DECIMALS,
CONF_ALPHA,
CONF_BELOW,
CONF_DISABLED_BY_DEFAULT,
CONF_EXPIRE_AFTER,
CONF_FILTERS,
CONF_FROM,
CONF_ICON,
CONF_ID,
CONF_INTERNAL,
CONF_ON_RAW_VALUE,
CONF_ON_VALUE,
CONF_ON_VALUE_RANGE,
@ -27,7 +25,6 @@ from esphome.const import (
CONF_TRIGGER_ID,
CONF_UNIT_OF_MEASUREMENT,
CONF_WINDOW_SIZE,
CONF_NAME,
CONF_MQTT_ID,
CONF_FORCE_UPDATE,
DEVICE_CLASS_EMPTY,
@ -59,6 +56,7 @@ from esphome.const import (
DEVICE_CLASS_VOLTAGE,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_helpers import setup_entity
from esphome.util import Registry
CODEOWNERS = ["@esphome/core"]
@ -136,7 +134,7 @@ def validate_datapoint(value):
# Base
sensor_ns = cg.esphome_ns.namespace("sensor")
Sensor = sensor_ns.class_("Sensor", cg.Nameable)
Sensor = sensor_ns.class_("Sensor", cg.EntityBase)
SensorPtr = Sensor.operator("ptr")
# Triggers
@ -180,12 +178,11 @@ validate_accuracy_decimals = cv.int_
validate_icon = cv.icon
validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
SENSOR_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
{
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTSensorComponent),
cv.GenerateID(): cv.declare_id(Sensor),
cv.Optional(CONF_UNIT_OF_MEASUREMENT): validate_unit_of_measurement,
cv.Optional(CONF_ICON): validate_icon,
cv.Optional(CONF_ACCURACY_DECIMALS): validate_accuracy_decimals,
cv.Optional(CONF_DEVICE_CLASS): validate_device_class,
cv.Optional(CONF_STATE_CLASS): validate_state_class,
@ -495,18 +492,14 @@ async def build_filters(config):
async def setup_sensor_core_(var, config):
cg.add(var.set_name(config[CONF_NAME]))
cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
if CONF_INTERNAL in config:
cg.add(var.set_internal(config[CONF_INTERNAL]))
await setup_entity(var, config)
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:
cg.add(var.set_icon(config[CONF_ICON]))
if CONF_ACCURACY_DECIMALS in config:
cg.add(var.set_accuracy_decimals(config[CONF_ACCURACY_DECIMALS]))
cg.add(var.set_force_update(config[CONF_FORCE_UPDATE]))

View file

@ -18,7 +18,7 @@ std::string state_class_to_string(StateClass state_class) {
}
}
Sensor::Sensor(const std::string &name) : Nameable(name), state(NAN), raw_state(NAN) {}
Sensor::Sensor(const std::string &name) : EntityBase(name), state(NAN), raw_state(NAN) {}
Sensor::Sensor() : Sensor("") {}
std::string Sensor::get_unit_of_measurement() {
@ -31,14 +31,6 @@ void Sensor::set_unit_of_measurement(const std::string &unit_of_measurement) {
}
std::string Sensor::unit_of_measurement() { return ""; }
std::string Sensor::get_icon() {
if (this->icon_.has_value())
return *this->icon_;
return this->icon();
}
void Sensor::set_icon(const std::string &icon) { this->icon_ = icon; }
std::string Sensor::icon() { return ""; }
int8_t Sensor::get_accuracy_decimals() {
if (this->accuracy_decimals_.has_value())
return *this->accuracy_decimals_;

View file

@ -2,6 +2,7 @@
#include "esphome/core/log.h"
#include "esphome/core/component.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/helpers.h"
#include "esphome/components/sensor/filter.h"
@ -43,7 +44,7 @@ std::string state_class_to_string(StateClass state_class);
*
* A sensor has unit of measurement and can use publish_state to send out a new value with the specified accuracy.
*/
class Sensor : public Nameable {
class Sensor : public EntityBase {
public:
explicit Sensor();
explicit Sensor(const std::string &name);
@ -53,11 +54,6 @@ class Sensor : public Nameable {
/// Manually set the unit of measurement.
void set_unit_of_measurement(const std::string &unit_of_measurement);
/// Get the icon. Uses the manual override if specified or the default value instead.
std::string get_icon();
/// Manually set the icon, for example "mdi:flash".
void set_icon(const std::string &icon);
/// Get the accuracy in decimals, using the manual override if set.
int8_t get_accuracy_decimals();
/// Manually set the accuracy in decimals.
@ -157,9 +153,6 @@ class Sensor : public Nameable {
/// Override this to set the default unit of measurement.
virtual std::string unit_of_measurement(); // NOLINT
/// Override this to set the default icon.
virtual std::string icon(); // NOLINT
/// Override this to set the default accuracy in decimals.
virtual int8_t accuracy_decimals(); // NOLINT
@ -178,7 +171,6 @@ class Sensor : public Nameable {
Filter *filter_list_{nullptr}; ///< Store all active filters.
optional<std::string> unit_of_measurement_; ///< Unit of measurement override
optional<std::string> icon_; ///< Icon override
optional<int8_t> accuracy_decimals_; ///< Accuracy in decimals override
optional<std::string> device_class_; ///< Device class override
optional<StateClass> state_class_{STATE_CLASS_NONE}; ///< State class override

View file

@ -4,24 +4,21 @@ from esphome import automation
from esphome.automation import Condition, maybe_simple_id
from esphome.components import mqtt
from esphome.const import (
CONF_DISABLED_BY_DEFAULT,
CONF_ICON,
CONF_ID,
CONF_INTERNAL,
CONF_INVERTED,
CONF_ON_TURN_OFF,
CONF_ON_TURN_ON,
CONF_TRIGGER_ID,
CONF_MQTT_ID,
CONF_NAME,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_helpers import setup_entity
CODEOWNERS = ["@esphome/core"]
IS_PLATFORM_COMPONENT = True
switch_ns = cg.esphome_ns.namespace("switch_")
Switch = switch_ns.class_("Switch", cg.Nameable)
Switch = switch_ns.class_("Switch", cg.EntityBase)
SwitchPtr = Switch.operator("ptr")
ToggleAction = switch_ns.class_("ToggleAction", automation.Action)
@ -39,10 +36,9 @@ SwitchTurnOffTrigger = switch_ns.class_(
icon = cv.icon
SWITCH_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
SWITCH_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend(
{
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTSwitchComponent),
cv.Optional(CONF_ICON): icon,
cv.Optional(CONF_INVERTED): cv.boolean,
cv.Optional(CONF_ON_TURN_ON): automation.validate_automation(
{
@ -59,12 +55,8 @@ SWITCH_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).exte
async def setup_switch_core_(var, config):
cg.add(var.set_name(config[CONF_NAME]))
cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
if CONF_INTERNAL in config:
cg.add(var.set_internal(config[CONF_INTERNAL]))
if CONF_ICON in config:
cg.add(var.set_icon(config[CONF_ICON]))
await setup_entity(var, config)
if CONF_INVERTED in config:
cg.add(var.set_inverted(config[CONF_INVERTED]))
for conf in config.get(CONF_ON_TURN_ON, []):

View file

@ -6,17 +6,9 @@ namespace switch_ {
static const char *const TAG = "switch";
std::string Switch::icon() { return ""; }
Switch::Switch(const std::string &name) : Nameable(name), state(false) {}
Switch::Switch(const std::string &name) : EntityBase(name), state(false) {}
Switch::Switch() : Switch("") {}
std::string Switch::get_icon() {
if (this->icon_.has_value())
return *this->icon_;
return this->icon();
}
void Switch::set_icon(const std::string &icon) { this->icon_ = icon; }
void Switch::turn_on() {
ESP_LOGD(TAG, "'%s' Turning ON.", this->get_name().c_str());
this->write_state(!this->inverted_);

View file

@ -1,6 +1,7 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/preferences.h"
#include "esphome/core/helpers.h"
@ -26,7 +27,7 @@ namespace switch_ {
* A switch is basically just a combination of a binary sensor (for reporting switch values)
* and a write_state method that writes a state to the hardware.
*/
class Switch : public Nameable {
class Switch : public EntityBase {
public:
explicit Switch();
explicit Switch(const std::string &name);
@ -70,12 +71,6 @@ class Switch : public Nameable {
*/
void set_inverted(bool inverted);
/// Set the icon for this switch. "" for no icon.
void set_icon(const std::string &icon);
/// Get the icon for this switch. Using icon() if not manually set
std::string get_icon();
/** Set callback for state changes.
*
* @param callback The void(bool) callback.
@ -104,18 +99,8 @@ class Switch : public Nameable {
*/
virtual void write_state(bool state) = 0;
/** Override this to set the Home Assistant icon for this switch.
*
* Return "" to disable this feature.
*
* @return The icon of this switch, for example "mdi:fan".
*/
virtual std::string icon(); // NOLINT
uint32_t hash_base() override;
optional<std::string> icon_{}; ///< The icon shown here. Not set means use default from switch. Empty means no icon.
CallbackManager<void(bool)> state_callback_{};
bool inverted_{false};
Deduplicator<bool> publish_dedup_;

View file

@ -3,21 +3,18 @@ import esphome.config_validation as cv
from esphome import automation
from esphome.components import mqtt
from esphome.const import (
CONF_DISABLED_BY_DEFAULT,
CONF_FILTERS,
CONF_ICON,
CONF_ID,
CONF_INTERNAL,
CONF_ON_VALUE,
CONF_ON_RAW_VALUE,
CONF_TRIGGER_ID,
CONF_MQTT_ID,
CONF_NAME,
CONF_STATE,
CONF_FROM,
CONF_TO,
)
from esphome.core import CORE, coroutine_with_priority
from esphome.cpp_helpers import setup_entity
from esphome.util import Registry
@ -25,7 +22,7 @@ IS_PLATFORM_COMPONENT = True
# pylint: disable=invalid-name
text_sensor_ns = cg.esphome_ns.namespace("text_sensor")
TextSensor = text_sensor_ns.class_("TextSensor", cg.Nameable)
TextSensor = text_sensor_ns.class_("TextSensor", cg.EntityBase)
TextSensorPtr = TextSensor.operator("ptr")
TextSensorStateTrigger = text_sensor_ns.class_(
@ -111,10 +108,9 @@ async def substitute_filter_to_code(config, filter_id):
icon = cv.icon
TEXT_SENSOR_SCHEMA = cv.NAMEABLE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
TEXT_SENSOR_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMPONENT_SCHEMA).extend(
{
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTTextSensor),
cv.Optional(CONF_ICON): icon,
cv.Optional(CONF_FILTERS): validate_filters,
cv.Optional(CONF_ON_VALUE): automation.validate_automation(
{
@ -137,12 +133,7 @@ async def build_filters(config):
async def setup_text_sensor_core_(var, config):
cg.add(var.set_name(config[CONF_NAME]))
cg.add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
if CONF_INTERNAL in config:
cg.add(var.set_internal(config[CONF_INTERNAL]))
if CONF_ICON in config:
cg.add(var.set_icon(config[CONF_ICON]))
await setup_entity(var, config)
if config.get(CONF_FILTERS): # must exist and not be empty
filters = await build_filters(config[CONF_FILTERS])

View file

@ -7,7 +7,7 @@ namespace text_sensor {
static const char *const TAG = "text_sensor";
TextSensor::TextSensor() : TextSensor("") {}
TextSensor::TextSensor(const std::string &name) : Nameable(name) {}
TextSensor::TextSensor(const std::string &name) : EntityBase(name) {}
void TextSensor::publish_state(const std::string &state) {
this->raw_state = state;
@ -68,14 +68,6 @@ void TextSensor::internal_send_state_to_frontend(const std::string &state) {
this->callback_.call(state);
}
void TextSensor::set_icon(const std::string &icon) { this->icon_ = icon; }
std::string TextSensor::get_icon() {
if (this->icon_.has_value())
return *this->icon_;
return this->icon();
}
std::string TextSensor::icon() { return ""; }
std::string TextSensor::unique_id() { return ""; }
bool TextSensor::has_state() { return this->has_state_; }
uint32_t TextSensor::hash_base() { return 334300109UL; }

View file

@ -1,6 +1,7 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/helpers.h"
#include "esphome/components/text_sensor/filter.h"
@ -18,7 +19,7 @@ namespace text_sensor {
} \
}
class TextSensor : public Nameable {
class TextSensor : public EntityBase {
public:
explicit TextSensor();
explicit TextSensor(const std::string &name);
@ -30,8 +31,6 @@ class TextSensor : public Nameable {
void publish_state(const std::string &state);
void set_icon(const std::string &icon);
/// Add a filter to the filter chain. Will be appended to the back.
void add_filter(Filter *filter);
@ -53,10 +52,6 @@ class TextSensor : public Nameable {
// ========== INTERNAL METHODS ==========
// (In most use cases you won't need these)
std::string get_icon();
virtual std::string icon();
virtual std::string unique_id();
bool has_state();
@ -71,7 +66,6 @@ class TextSensor : public Nameable {
Filter *filter_list_{nullptr}; ///< Store all active filters.
optional<std::string> icon_;
bool has_state_{false};
};

View file

@ -2,12 +2,14 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor, time
from esphome.const import (
CONF_ICON,
CONF_ID,
CONF_TIME_ID,
DEVICE_CLASS_ENERGY,
CONF_METHOD,
STATE_CLASS_TOTAL_INCREASING,
)
from esphome.core.entity_helpers import inherit_property_from
DEPENDENCIES = ["time"]
@ -45,6 +47,18 @@ CONFIG_SCHEMA = (
.extend(cv.COMPONENT_SCHEMA)
)
FINAL_VALIDATE_SCHEMA = cv.All(
cv.Schema(
{
cv.Required(CONF_ID): cv.use_id(TotalDailyEnergy),
cv.Optional(CONF_ICON): cv.icon,
cv.Required(CONF_POWER_ID): cv.use_id(sensor.Sensor),
},
extra=cv.ALLOW_EXTRA,
),
inherit_property_from(CONF_ICON, CONF_POWER_ID),
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])

View file

@ -25,7 +25,6 @@ class TotalDailyEnergy : public sensor::Sensor, public Component {
void dump_config() override;
float get_setup_priority() const override { return setup_priority::DATA; }
std::string unit_of_measurement() override { return this->parent_->get_unit_of_measurement() + "h"; }
std::string icon() override { return this->parent_->get_icon(); }
int8_t accuracy_decimals() override { return this->parent_->get_accuracy_decimals() + 2; }
void loop() override;

View file

@ -224,7 +224,7 @@ class TSL2591Component : public PollingComponent, public i2c::I2CDevice {
float get_setup_priority() const override;
protected:
const char *name_; // TODO: extend esphome::Nameable
const char *name_;
sensor::Sensor *full_spectrum_sensor_;
sensor::Sensor *infrared_sensor_;
sensor::Sensor *visible_sensor_;

View file

@ -3,6 +3,7 @@
#include "web_server.h"
#include "esphome/core/log.h"
#include "esphome/core/application.h"
#include "esphome/core/entity_base.h"
#include "esphome/core/util.h"
#include "esphome/components/json/json_util.h"
#include "esphome/components/network/util.h"
@ -28,8 +29,8 @@ namespace web_server {
static const char *const TAG = "web_server";
void write_row(AsyncResponseStream *stream, Nameable *obj, const std::string &klass, const std::string &action,
const std::function<void(AsyncResponseStream &stream, Nameable *obj)> &action_func = nullptr) {
void write_row(AsyncResponseStream *stream, EntityBase *obj, const std::string &klass, const std::string &action,
const std::function<void(AsyncResponseStream &stream, EntityBase *obj)> &action_func = nullptr) {
if (obj->is_internal())
return;
stream->print("<tr class=\"");
@ -225,7 +226,7 @@ void WebServer::handle_index_request(AsyncWebServerRequest *request) {
#ifdef USE_SELECT
for (auto *obj : App.get_selects())
write_row(stream, obj, "select", "", [](AsyncResponseStream &stream, Nameable *obj) {
write_row(stream, obj, "select", "", [](AsyncResponseStream &stream, EntityBase *obj) {
select::Select *select = (select::Select *) obj;
stream.print("<select>");
stream.print("<option></option>");

View file

@ -18,6 +18,7 @@ from esphome.const import (
CONF_COMMAND_TOPIC,
CONF_DISABLED_BY_DEFAULT,
CONF_DISCOVERY,
CONF_ICON,
CONF_ID,
CONF_INTERNAL,
CONF_NAME,
@ -1476,7 +1477,7 @@ class OnlyWith(Optional):
pass
def _nameable_validator(config):
def _entity_base_validator(config):
if CONF_NAME not in config and CONF_ID not in config:
raise Invalid("At least one of 'id:' or 'name:' is required!")
if CONF_NAME not in config:
@ -1587,15 +1588,16 @@ MQTT_COMMAND_COMPONENT_SCHEMA = MQTT_COMPONENT_SCHEMA.extend(
}
)
NAMEABLE_SCHEMA = Schema(
ENTITY_BASE_SCHEMA = Schema(
{
Optional(CONF_NAME): string,
Optional(CONF_INTERNAL): boolean,
Optional(CONF_DISABLED_BY_DEFAULT, default=False): boolean,
Optional(CONF_ICON): icon,
}
)
NAMEABLE_SCHEMA.add_extra(_nameable_validator)
ENTITY_BASE_SCHEMA.add_extra(_entity_base_validator)
COMPONENT_SCHEMA = Schema({Optional(CONF_SETUP_PRIORITY): float_})

View file

@ -177,26 +177,6 @@ void PollingComponent::call_setup() {
uint32_t PollingComponent::get_update_interval() const { return this->update_interval_; }
void PollingComponent::set_update_interval(uint32_t update_interval) { this->update_interval_ = update_interval; }
const std::string &Nameable::get_name() const { return this->name_; }
void Nameable::set_name(const std::string &name) {
this->name_ = name;
this->calc_object_id_();
}
Nameable::Nameable(std::string name) : name_(std::move(name)) { this->calc_object_id_(); }
const std::string &Nameable::get_object_id() { return this->object_id_; }
bool Nameable::is_internal() const { return this->internal_; }
void Nameable::set_internal(bool internal) { this->internal_ = internal; }
void Nameable::calc_object_id_() {
this->object_id_ = sanitize_string_allowlist(to_lowercase_underscore(this->name_), HOSTNAME_CHARACTER_ALLOWLIST);
// FNV-1 hash
this->object_id_hash_ = fnv1_hash(this->object_id_);
}
uint32_t Nameable::get_object_id_hash() { return this->object_id_hash_; }
bool Nameable::is_disabled_by_default() const { return this->disabled_by_default_; }
void Nameable::set_disabled_by_default(bool disabled_by_default) { this->disabled_by_default_ = disabled_by_default; }
WarnIfComponentBlockingGuard::WarnIfComponentBlockingGuard(Component *component)
: started_(millis()), component_(component) {}
WarnIfComponentBlockingGuard::~WarnIfComponentBlockingGuard() {

View file

@ -264,40 +264,6 @@ class PollingComponent : public Component {
uint32_t update_interval_;
};
/// Helper class that enables naming of objects so that it doesn't have to be re-implement every time.
class Nameable {
public:
Nameable() : Nameable("") {}
explicit Nameable(std::string name);
const std::string &get_name() const;
void set_name(const std::string &name);
/// Get the sanitized name of this nameable as an ID. Caching it internally.
const std::string &get_object_id();
uint32_t get_object_id_hash();
bool is_internal() const;
void set_internal(bool internal);
/** Check if this object is declared to be disabled by default.
*
* That means that when the device gets added to Home Assistant (or other clients) it should
* not be added to the default view by default, and a user action is necessary to manually add it.
*/
bool is_disabled_by_default() const;
void set_disabled_by_default(bool disabled_by_default);
protected:
virtual uint32_t hash_base() = 0;
void calc_object_id_();
std::string name_;
std::string object_id_;
uint32_t object_id_hash_;
bool internal_{false};
bool disabled_by_default_{false};
};
class WarnIfComponentBlockingGuard {
public:
WarnIfComponentBlockingGuard(Component *component);

View file

@ -0,0 +1,40 @@
#include "esphome/core/entity_base.h"
#include "esphome/core/helpers.h"
namespace esphome {
static const char *const TAG = "entity_base";
EntityBase::EntityBase(std::string name) : name_(std::move(name)) { this->calc_object_id_(); }
// Entity Name
const std::string &EntityBase::get_name() const { return this->name_; }
void EntityBase::set_name(const std::string &name) {
this->name_ = name;
this->calc_object_id_();
}
// Entity Internal
bool EntityBase::is_internal() const { return this->internal_; }
void EntityBase::set_internal(bool internal) { this->internal_ = internal; }
// Entity Disabled by Default
bool EntityBase::is_disabled_by_default() const { return this->disabled_by_default_; }
void EntityBase::set_disabled_by_default(bool disabled_by_default) { this->disabled_by_default_ = disabled_by_default; }
// Entity Icon
const std::string &EntityBase::get_icon() const { return this->icon_; }
void EntityBase::set_icon(const std::string &name) { this->icon_ = name; }
// Entity Object ID
const std::string &EntityBase::get_object_id() { return this->object_id_; }
// Calculate Object ID Hash from Entity Name
void EntityBase::calc_object_id_() {
this->object_id_ = sanitize_string_allowlist(to_lowercase_underscore(this->name_), HOSTNAME_CHARACTER_ALLOWLIST);
// FNV-1 hash
this->object_id_hash_ = fnv1_hash(this->object_id_);
}
uint32_t EntityBase::get_object_id_hash() { return this->object_id_hash_; }
} // namespace esphome

View file

@ -0,0 +1,50 @@
#pragma once
#include <string>
#include <cstdint>
namespace esphome {
// The generic Entity base class that provides an interface common to all Entities.
class EntityBase {
public:
EntityBase() : EntityBase("") {}
explicit EntityBase(std::string name);
// Get/set the name of this Entity
const std::string &get_name() const;
void set_name(const std::string &name);
// Get the sanitized name of this Entity as an ID. Caching it internally.
const std::string &get_object_id();
// Get the unique Object ID of this Entity
uint32_t get_object_id_hash();
// Get/set whether this Entity should be hidden from outside of ESPHome
bool is_internal() const;
void set_internal(bool internal);
// Check if this object is declared to be disabled by default.
// That means that when the device gets added to Home Assistant (or other clients) it should
// not be added to the default view by default, and a user action is necessary to manually add it.
bool is_disabled_by_default() const;
void set_disabled_by_default(bool disabled_by_default);
// Get/set this entity's icon
const std::string &get_icon() const;
void set_icon(const std::string &name);
protected:
virtual uint32_t hash_base() = 0;
void calc_object_id_();
std::string name_;
std::string object_id_;
std::string icon_;
uint32_t object_id_hash_;
bool internal_{false};
bool disabled_by_default_{false};
};
} // namespace esphome

View file

@ -0,0 +1,32 @@
import esphome.final_validate as fv
from esphome.const import CONF_ID
def inherit_property_from(property_to_inherit, parent_id_property):
"""Validator that inherits a configuration property from another entity, for use with FINAL_VALIDATE_SCHEMA.
If a property is already set, it will not be inherited.
Keyword arguments:
property_to_inherit -- the name of the property to inherit, e.g. CONF_ICON
parent_id_property -- the name of the property that holds the ID of the parent, e.g. CONF_POWER_ID
"""
def inherit_property(config):
if property_to_inherit not in config:
fconf = fv.full_config.get()
# Get config for the parent entity
path = fconf.get_path_for_id(config[parent_id_property])[:-1]
parent_config = fconf.get_config_for_path(path)
# If parent sensor has the property set, inherit it
if property_to_inherit in parent_config:
path = fconf.get_path_for_id(config[CONF_ID])[:-1]
this_config = fconf.get_config_for_path(path)
this_config[property_to_inherit] = parent_config[property_to_inherit]
return config
return inherit_property

View file

@ -1,6 +1,10 @@
import logging
from esphome.const import (
CONF_DISABLED_BY_DEFAULT,
CONF_ICON,
CONF_INTERNAL,
CONF_NAME,
CONF_SETUP_PRIORITY,
CONF_UPDATE_INTERVAL,
CONF_TYPE_ID,
@ -90,6 +94,16 @@ async def register_parented(var, value):
add(var.set_parent(paren))
async def setup_entity(var, config):
"""Set up generic properties of an Entity"""
add(var.set_name(config[CONF_NAME]))
add(var.set_disabled_by_default(config[CONF_DISABLED_BY_DEFAULT]))
if CONF_INTERNAL in config:
add(var.set_internal(config[CONF_INTERNAL]))
if CONF_ICON in config:
add(var.set_icon(config[CONF_ICON]))
def extract_registry_entry_config(registry, full_config):
# type: (Registry, ConfigType) -> RegistryEntry
key, config = next((k, v) for k, v in full_config.items() if k in registry)

View file

@ -19,7 +19,7 @@ const_char_ptr = global_ns.namespace("const char *")
NAN = global_ns.namespace("NAN")
esphome_ns = global_ns # using namespace esphome;
App = esphome_ns.App
Nameable = esphome_ns.class_("Nameable")
EntityBase = esphome_ns.class_("EntityBase")
Component = esphome_ns.class_("Component")
ComponentPtr = Component.operator("ptr")
PollingComponent = esphome_ns.class_("PollingComponent", Component)

View file

@ -1708,6 +1708,7 @@ climate:
name: Anova cooker
ble_client_id: ble_blah
unit_of_measurement: c
icon: mdi:stove
script:
- id: climate_custom
@ -1986,6 +1987,7 @@ fan:
direction_output: gpio_26
- platform: speed
id: fan_speed
icon: mdi:weather-windy
output: pca_6
speed_count: 10
name: 'Living Room Fan 2'
@ -2287,6 +2289,7 @@ cover:
name: 'Test AM43'
id: am43_test
ble_client_id: ble_foo
icon: mdi:blinds
debug:

View file

@ -51,6 +51,7 @@ binary_sensor:
- platform: gpio
pin: GPIO0
id: io0_button
icon: mdi:gesture-tap-button
tlc5947:
data_pin: GPIO12

View file

@ -59,7 +59,7 @@ from esphome import codegen as cg
"NAN",
"esphome_ns",
"App",
"Nameable",
"EntityBase",
"Component",
"ComponentPtr",
# from cpp_types