From 36c0e2416d63e44f3876410fa668676e41347fe1 Mon Sep 17 00:00:00 2001 From: Regev Brody Date: Sun, 26 Mar 2023 23:01:35 +0300 Subject: [PATCH 1/8] add select_schema to select component (#4545) * add select_schema to select component * add select_schema to select component * fix cr --- esphome/components/copy/select/__init__.py | 15 ++++++---- .../modbus_controller/select/__init__.py | 5 ++-- esphome/components/select/__init__.py | 29 +++++++++++++++++-- .../components/template/select/__init__.py | 7 +++-- esphome/components/tuya/select/__init__.py | 21 ++++++++------ 5 files changed, 55 insertions(+), 22 deletions(-) diff --git a/esphome/components/copy/select/__init__.py b/esphome/components/copy/select/__init__.py index 7d4c1c7705..6254ed03e1 100644 --- a/esphome/components/copy/select/__init__.py +++ b/esphome/components/copy/select/__init__.py @@ -14,12 +14,15 @@ from .. import copy_ns CopySelect = copy_ns.class_("CopySelect", select.Select, cg.Component) -CONFIG_SCHEMA = select.SELECT_SCHEMA.extend( - { - cv.GenerateID(): cv.declare_id(CopySelect), - cv.Required(CONF_SOURCE_ID): cv.use_id(select.Select), - } -).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + select.select_schema(CopySelect) + .extend( + { + cv.Required(CONF_SOURCE_ID): cv.use_id(select.Select), + } + ) + .extend(cv.COMPONENT_SCHEMA) +) FINAL_VALIDATE_SCHEMA = cv.All( inherit_property_from(CONF_ICON, CONF_SOURCE_ID), diff --git a/esphome/components/modbus_controller/select/__init__.py b/esphome/components/modbus_controller/select/__init__.py index f8ef61ddc4..5692fea3e3 100644 --- a/esphome/components/modbus_controller/select/__init__.py +++ b/esphome/components/modbus_controller/select/__init__.py @@ -64,9 +64,10 @@ INTEGER_SENSOR_VALUE_TYPE = { } CONFIG_SCHEMA = cv.All( - select.SELECT_SCHEMA.extend(cv.COMPONENT_SCHEMA).extend( + select.select_schema(ModbusSelect) + .extend(cv.COMPONENT_SCHEMA) + .extend( { - cv.GenerateID(): cv.declare_id(ModbusSelect), cv.GenerateID(CONF_MODBUS_CONTROLLER_ID): cv.use_id(ModbusController), cv.Required(CONF_ADDRESS): cv.positive_int, cv.Optional(CONF_VALUE_TYPE, default="U_WORD"): cv.enum( diff --git a/esphome/components/select/__init__.py b/esphome/components/select/__init__.py index b505d89c6f..760f7600b7 100644 --- a/esphome/components/select/__init__.py +++ b/esphome/components/select/__init__.py @@ -3,6 +3,8 @@ import esphome.config_validation as cv from esphome import automation from esphome.components import mqtt from esphome.const import ( + CONF_ENTITY_CATEGORY, + CONF_ICON, CONF_ID, CONF_ON_VALUE, CONF_OPTION, @@ -14,6 +16,7 @@ from esphome.const import ( CONF_INDEX, ) from esphome.core import CORE, coroutine_with_priority +from esphome.cpp_generator import MockObjClass from esphome.cpp_helpers import setup_entity CODEOWNERS = ["@esphome/core"] @@ -43,8 +46,6 @@ SELECT_OPERATION_OPTIONS = { "LAST": SelectOperation.SELECT_OP_LAST, } -icon = cv.icon - SELECT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).extend( { @@ -58,6 +59,30 @@ SELECT_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA).e } ) +_UNDEF = object() + + +def select_schema( + class_: MockObjClass = _UNDEF, + *, + entity_category: str = _UNDEF, + icon: str = _UNDEF, +): + schema = cv.Schema({}) + if class_ is not _UNDEF: + schema = schema.extend({cv.GenerateID(): cv.declare_id(class_)}) + if entity_category is not _UNDEF: + schema = schema.extend( + { + cv.Optional( + CONF_ENTITY_CATEGORY, default=entity_category + ): cv.entity_category + } + ) + if icon is not _UNDEF: + schema = schema.extend({cv.Optional(CONF_ICON, default=icon): cv.icon}) + return SELECT_SCHEMA.extend(schema) + async def setup_select_core_(var, config, *, options: list[str]): await setup_entity(var, config) diff --git a/esphome/components/template/select/__init__.py b/esphome/components/template/select/__init__.py index 4eba77119d..d116cbb8ae 100644 --- a/esphome/components/template/select/__init__.py +++ b/esphome/components/template/select/__init__.py @@ -43,9 +43,9 @@ def validate(config): CONFIG_SCHEMA = cv.All( - select.SELECT_SCHEMA.extend( + select.select_schema(TemplateSelect) + .extend( { - cv.GenerateID(): cv.declare_id(TemplateSelect), cv.Required(CONF_OPTIONS): cv.All( cv.ensure_list(cv.string_strict), cv.Length(min=1) ), @@ -55,7 +55,8 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_INITIAL_OPTION): cv.string_strict, cv.Optional(CONF_RESTORE_VALUE): cv.boolean, } - ).extend(cv.polling_component_schema("60s")), + ) + .extend(cv.polling_component_schema("60s")), validate, ) diff --git a/esphome/components/tuya/select/__init__.py b/esphome/components/tuya/select/__init__.py index 3d65eda301..dc78b2c3db 100644 --- a/esphome/components/tuya/select/__init__.py +++ b/esphome/components/tuya/select/__init__.py @@ -25,15 +25,18 @@ def ensure_option_map(value): return value -CONFIG_SCHEMA = select.SELECT_SCHEMA.extend( - { - cv.GenerateID(): cv.declare_id(TuyaSelect), - cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya), - cv.Required(CONF_ENUM_DATAPOINT): cv.uint8_t, - cv.Required(CONF_OPTIONS): ensure_option_map, - cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean, - } -).extend(cv.COMPONENT_SCHEMA) +CONFIG_SCHEMA = ( + select.select_schema(TuyaSelect) + .extend( + { + cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya), + cv.Required(CONF_ENUM_DATAPOINT): cv.uint8_t, + cv.Required(CONF_OPTIONS): ensure_option_map, + cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean, + } + ) + .extend(cv.COMPONENT_SCHEMA) +) async def to_code(config): From 9ee661c1e4efb2bcf11f3490942332d0a5519535 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Sun, 26 Mar 2023 11:48:56 -1000 Subject: [PATCH 2/8] Add ability to clear the gatt cache (#4621) * Add ability to clear the gatt cache With BlueZ we can fully clear the cache when something goes wrong with the services, however since this is also a cache on the ESP32 we need to be able to clear the on device cache as well for the proxies since if something goes wrong with the service resolution it can cache the bad resolution on NVS forever. Our current client implementation is limited to clearing the memory cache in Home Assistant https://github.com/home-assistant/core/blob/89355e087952417a6824507fd3b197f9d0520e19/homeassistant/components/esphome/bluetooth/client.py#L512 related issue https://github.com/esphome/issues/issues/4156 https://github.com/esphome/aioesphomeapi/pull/410 * naming * lint * lint * naming * naming * naming * 88 now that 87 is taken * make const * Update esphome/components/api/api_frame_helper.cpp --- esphome/components/api/api.proto | 11 +++++ esphome/components/api/api_connection.cpp | 4 +- esphome/components/api/api_pb2.cpp | 45 +++++++++++++++++++ esphome/components/api/api_pb2.h | 14 ++++++ esphome/components/api/api_pb2_service.cpp | 8 ++++ esphome/components/api/api_pb2_service.h | 3 ++ esphome/components/api/api_server.cpp | 11 +++++ esphome/components/api/api_server.h | 1 + .../bluetooth_proxy/bluetooth_proxy.cpp | 7 +++ .../bluetooth_proxy/bluetooth_proxy.h | 8 ++++ 10 files changed, 111 insertions(+), 1 deletion(-) diff --git a/esphome/components/api/api.proto b/esphome/components/api/api.proto index 1cebdd0cbe..a64aac52e2 100644 --- a/esphome/components/api/api.proto +++ b/esphome/components/api/api.proto @@ -1156,6 +1156,7 @@ enum BluetoothDeviceRequestType { BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR = 3; BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE = 4; BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5; + BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6; } message BluetoothDeviceRequest { @@ -1359,3 +1360,13 @@ message BluetoothDeviceUnpairingResponse { bool success = 2; int32 error = 3; } + +message BluetoothDeviceClearCacheResponse { + option (id) = 88; + option (source) = SOURCE_SERVER; + option (ifdef) = "USE_BLUETOOTH_PROXY"; + + uint64 address = 1; + bool success = 2; + int32 error = 3; +} diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 40a5a230a5..e607f45e3f 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -953,7 +953,9 @@ DeviceInfoResponse APIConnection::device_info(const DeviceInfoRequest &msg) { resp.webserver_port = USE_WEBSERVER_PORT; #endif #ifdef USE_BLUETOOTH_PROXY - resp.bluetooth_proxy_version = bluetooth_proxy::global_bluetooth_proxy->has_active() ? 4 : 1; + resp.bluetooth_proxy_version = bluetooth_proxy::global_bluetooth_proxy->has_active() + ? bluetooth_proxy::ACTIVE_CONNECTIONS_VERSION + : bluetooth_proxy::PASSIVE_ONLY_VERSION; #endif return resp; } diff --git a/esphome/components/api/api_pb2.cpp b/esphome/components/api/api_pb2.cpp index 381f8b3c46..43587469af 100644 --- a/esphome/components/api/api_pb2.cpp +++ b/esphome/components/api/api_pb2.cpp @@ -400,6 +400,8 @@ const char *proto_enum_to_string(enums::Bluet return "BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE"; case enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE: return "BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE"; + case enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE: + return "BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE"; default: return "UNKNOWN"; } @@ -6060,6 +6062,49 @@ void BluetoothDeviceUnpairingResponse::dump_to(std::string &out) const { out.append("}"); } #endif +bool BluetoothDeviceClearCacheResponse::decode_varint(uint32_t field_id, ProtoVarInt value) { + switch (field_id) { + case 1: { + this->address = value.as_uint64(); + return true; + } + case 2: { + this->success = value.as_bool(); + return true; + } + case 3: { + this->error = value.as_int32(); + return true; + } + default: + return false; + } +} +void BluetoothDeviceClearCacheResponse::encode(ProtoWriteBuffer buffer) const { + buffer.encode_uint64(1, this->address); + buffer.encode_bool(2, this->success); + buffer.encode_int32(3, this->error); +} +#ifdef HAS_PROTO_MESSAGE_DUMP +void BluetoothDeviceClearCacheResponse::dump_to(std::string &out) const { + __attribute__((unused)) char buffer[64]; + out.append("BluetoothDeviceClearCacheResponse {\n"); + out.append(" address: "); + sprintf(buffer, "%llu", this->address); + out.append(buffer); + out.append("\n"); + + out.append(" success: "); + out.append(YESNO(this->success)); + out.append("\n"); + + out.append(" error: "); + sprintf(buffer, "%d", this->error); + out.append(buffer); + out.append("\n"); + out.append("}"); +} +#endif } // namespace api } // namespace esphome diff --git a/esphome/components/api/api_pb2.h b/esphome/components/api/api_pb2.h index e9025142e9..ff581cac6f 100644 --- a/esphome/components/api/api_pb2.h +++ b/esphome/components/api/api_pb2.h @@ -163,6 +163,7 @@ enum BluetoothDeviceRequestType : uint32_t { BLUETOOTH_DEVICE_REQUEST_TYPE_UNPAIR = 3, BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITH_CACHE = 4, BLUETOOTH_DEVICE_REQUEST_TYPE_CONNECT_V3_WITHOUT_CACHE = 5, + BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE = 6, }; } // namespace enums @@ -1554,6 +1555,19 @@ class BluetoothDeviceUnpairingResponse : public ProtoMessage { protected: bool decode_varint(uint32_t field_id, ProtoVarInt value) override; }; +class BluetoothDeviceClearCacheResponse : public ProtoMessage { + public: + uint64_t address{0}; + bool success{false}; + int32_t error{0}; + void encode(ProtoWriteBuffer buffer) const override; +#ifdef HAS_PROTO_MESSAGE_DUMP + void dump_to(std::string &out) const override; +#endif + + protected: + bool decode_varint(uint32_t field_id, ProtoVarInt value) override; +}; } // namespace api } // namespace esphome diff --git a/esphome/components/api/api_pb2_service.cpp b/esphome/components/api/api_pb2_service.cpp index 7ee9e56192..73015fa914 100644 --- a/esphome/components/api/api_pb2_service.cpp +++ b/esphome/components/api/api_pb2_service.cpp @@ -441,6 +441,14 @@ bool APIServerConnectionBase::send_bluetooth_device_unpairing_response(const Blu return this->send_message_(msg, 86); } #endif +#ifdef USE_BLUETOOTH_PROXY +bool APIServerConnectionBase::send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg) { +#ifdef HAS_PROTO_MESSAGE_DUMP + ESP_LOGVV(TAG, "send_bluetooth_device_clear_cache_response: %s", msg.dump().c_str()); +#endif + return this->send_message_(msg, 88); +} +#endif bool APIServerConnectionBase::read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) { switch (msg_type) { case 1: { diff --git a/esphome/components/api/api_pb2_service.h b/esphome/components/api/api_pb2_service.h index f1879b2dba..7f19292ff3 100644 --- a/esphome/components/api/api_pb2_service.h +++ b/esphome/components/api/api_pb2_service.h @@ -215,6 +215,9 @@ class APIServerConnectionBase : public ProtoService { #endif #ifdef USE_BLUETOOTH_PROXY bool send_bluetooth_device_unpairing_response(const BluetoothDeviceUnpairingResponse &msg); +#endif +#ifdef USE_BLUETOOTH_PROXY + bool send_bluetooth_device_clear_cache_response(const BluetoothDeviceClearCacheResponse &msg); #endif protected: bool read_message(uint32_t msg_size, uint32_t msg_type, uint8_t *msg_data) override; diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index acde0966ba..c60766b364 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -331,6 +331,17 @@ void APIServer::send_bluetooth_device_unpairing(uint64_t address, bool success, } } +void APIServer::send_bluetooth_device_clear_cache(uint64_t address, bool success, esp_err_t error) { + BluetoothDeviceClearCacheResponse call; + call.address = address; + call.success = success; + call.error = error; + + for (auto &client : this->clients_) { + client->send_bluetooth_device_clear_cache_response(call); + } +} + void APIServer::send_bluetooth_connections_free(uint8_t free, uint8_t limit) { BluetoothConnectionsFreeResponse call; call.free = free; diff --git a/esphome/components/api/api_server.h b/esphome/components/api/api_server.h index 5f92e6b058..db87affdb8 100644 --- a/esphome/components/api/api_server.h +++ b/esphome/components/api/api_server.h @@ -80,6 +80,7 @@ class APIServer : public Component, public Controller { void send_bluetooth_device_connection(uint64_t address, bool connected, uint16_t mtu = 0, esp_err_t error = ESP_OK); void send_bluetooth_device_pairing(uint64_t address, bool paired, esp_err_t error = ESP_OK); void send_bluetooth_device_unpairing(uint64_t address, bool success, esp_err_t error = ESP_OK); + void send_bluetooth_device_clear_cache(uint64_t address, bool success, esp_err_t error = ESP_OK); void send_bluetooth_connections_free(uint8_t free, uint8_t limit); void send_bluetooth_gatt_read_response(const BluetoothGATTReadResponse &call); void send_bluetooth_gatt_write_response(const BluetoothGATTWriteResponse &call); diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp index 55fabf05ef..76950c944e 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.cpp @@ -306,6 +306,13 @@ void BluetoothProxy::bluetooth_device_request(const api::BluetoothDeviceRequest api::global_api_server->send_bluetooth_device_unpairing(msg.address, ret == ESP_OK, ret); break; } + case api::enums::BLUETOOTH_DEVICE_REQUEST_TYPE_CLEAR_CACHE: { + esp_bd_addr_t address; + uint64_to_bd_addr(msg.address, address); + esp_err_t ret = esp_ble_gattc_cache_clean(address); + api::global_api_server->send_bluetooth_device_clear_cache(msg.address, ret == ESP_OK, ret); + break; + } } } diff --git a/esphome/components/bluetooth_proxy/bluetooth_proxy.h b/esphome/components/bluetooth_proxy/bluetooth_proxy.h index b99e9a8527..a582abc8a3 100644 --- a/esphome/components/bluetooth_proxy/bluetooth_proxy.h +++ b/esphome/components/bluetooth_proxy/bluetooth_proxy.h @@ -68,6 +68,14 @@ class BluetoothProxy : public esp32_ble_tracker::ESPBTDeviceListener, public Com extern BluetoothProxy *global_bluetooth_proxy; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables) +// Version 1: Initial version without active connections +// Version 2: Support for active connections +// Version 3: New connection API +// Version 4: Pairing support +// Version 5: Cache clear support +static const uint32_t ACTIVE_CONNECTIONS_VERSION = 5; +static const uint32_t PASSIVE_ONLY_VERSION = 1; + } // namespace bluetooth_proxy } // namespace esphome From 29e7d00894cad7086dd7910ce3a216972c3d015c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 27 Mar 2023 10:51:37 +1300 Subject: [PATCH 3/8] Bump actions/stale from 7 to 8 (#4615) Bumps [actions/stale](https://github.com/actions/stale) from 7 to 8. - [Release notes](https://github.com/actions/stale/releases) - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/stale/compare/v7...v8) --- updated-dependencies: - dependency-name: actions/stale dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/stale.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index a2ba086394..f5d291b49f 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -18,7 +18,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v7 + - uses: actions/stale@v8 with: days-before-pr-stale: 90 days-before-pr-close: 7 @@ -38,7 +38,7 @@ jobs: close-issues: runs-on: ubuntu-latest steps: - - uses: actions/stale@v7 + - uses: actions/stale@v8 with: days-before-pr-stale: -1 days-before-pr-close: -1 From 806e43c34c09fe28a3cc0d7dcf1609a30781d4f0 Mon Sep 17 00:00:00 2001 From: tracestep <16390082+tracestep@users.noreply.github.com> Date: Sun, 26 Mar 2023 18:59:57 -0300 Subject: [PATCH 4/8] SX1509 minimum loop period (fixes esphome/issues#4325) (#4613) * Minimum loop period (fixes esphome/issues#4325) * clang-tidy suggestions * More clang-tidy suggestions --- esphome/components/sx1509/sx1509.cpp | 3 +++ esphome/components/sx1509/sx1509.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/esphome/components/sx1509/sx1509.cpp b/esphome/components/sx1509/sx1509.cpp index 60cbae6aa6..d0a84b99ff 100644 --- a/esphome/components/sx1509/sx1509.cpp +++ b/esphome/components/sx1509/sx1509.cpp @@ -42,6 +42,9 @@ void SX1509Component::dump_config() { void SX1509Component::loop() { if (this->has_keypad_) { + if (millis() - this->last_loop_timestamp_ < min_loop_period_) + return; + this->last_loop_timestamp_ = millis(); uint16_t key_data = this->read_key_data(); for (auto *binary_sensor : this->keypad_binary_sensors_) binary_sensor->process(key_data); diff --git a/esphome/components/sx1509/sx1509.h b/esphome/components/sx1509/sx1509.h index 50230b1086..8e3b41e233 100644 --- a/esphome/components/sx1509/sx1509.h +++ b/esphome/components/sx1509/sx1509.h @@ -69,6 +69,9 @@ class SX1509Component : public Component, public i2c::I2CDevice { uint8_t debounce_time_ = 1; std::vector keypad_binary_sensors_; + uint32_t last_loop_timestamp_ = 0; + const uint32_t min_loop_period_ = 15; // ms + void setup_keypad_(); void set_debounce_config_(uint8_t config_value); void set_debounce_time_(uint8_t time); From e542e75b9e60bf285428ef14c9e87134ba5b68e5 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 27 Mar 2023 11:44:56 +1300 Subject: [PATCH 5/8] Require step to be set when calling register_number (#4622) --- esphome/components/number/__init__.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/esphome/components/number/__init__.py b/esphome/components/number/__init__.py index 70c53cace3..7db973612b 100644 --- a/esphome/components/number/__init__.py +++ b/esphome/components/number/__init__.py @@ -204,14 +204,13 @@ def number_schema( async def setup_number_core_( - var, config, *, min_value: float, max_value: float, step: Optional[float] + var, config, *, min_value: float, max_value: float, step: float ): await setup_entity(var, config) cg.add(var.traits.set_min_value(min_value)) cg.add(var.traits.set_max_value(max_value)) - if step is not None: - cg.add(var.traits.set_step(step)) + cg.add(var.traits.set_step(step)) cg.add(var.traits.set_mode(config[CONF_MODE])) @@ -239,7 +238,7 @@ async def setup_number_core_( async def register_number( - var, config, *, min_value: float, max_value: float, step: Optional[float] = None + var, config, *, min_value: float, max_value: float, step: float ): if not CORE.has_id(config[CONF_ID]): var = cg.Pvariable(config[CONF_ID], var) From 56504692af70d34a9b2f1219fac0890d64f1f84a Mon Sep 17 00:00:00 2001 From: Berend Haan Date: Mon, 27 Mar 2023 00:48:17 +0200 Subject: [PATCH 6/8] Lower range of CONF_FREQUENCY (#4619) --- esphome/components/esp32_camera/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esphome/components/esp32_camera/__init__.py b/esphome/components/esp32_camera/__init__.py index d6a744d24d..4cbdf7ca5c 100644 --- a/esphome/components/esp32_camera/__init__.py +++ b/esphome/components/esp32_camera/__init__.py @@ -156,7 +156,7 @@ CONFIG_SCHEMA = cv.ENTITY_BASE_SCHEMA.extend( { cv.Required(CONF_PIN): pins.internal_gpio_input_pin_number, cv.Optional(CONF_FREQUENCY, default="20MHz"): cv.All( - cv.frequency, cv.Range(min=10e6, max=20e6) + cv.frequency, cv.Range(min=8e6, max=20e6) ), } ), From c2756d57d8878dc7d91f36bcba8a0cf3d0b9d7a1 Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 27 Mar 2023 11:49:09 +1300 Subject: [PATCH 7/8] Allow entity names to be set to None (#4607) * Allow entity names to be set to None so they take on the device friendly_name automatically * Use empty --- esphome/components/api/api_connection.cpp | 39 ++++++++++++++------- esphome/components/esp32_improv/__init__.py | 10 +----- esphome/config_validation.py | 29 ++++++++++++++- esphome/core/entity_base.cpp | 9 ++++- esphome/core/entity_base.h | 4 +++ 5 files changed, 67 insertions(+), 24 deletions(-) diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index e607f45e3f..77ba96291a 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -180,7 +180,8 @@ bool APIConnection::send_binary_sensor_info(binary_sensor::BinarySensor *binary_ ListEntitiesBinarySensorResponse msg; msg.object_id = binary_sensor->get_object_id(); msg.key = binary_sensor->get_object_id_hash(); - msg.name = binary_sensor->get_name(); + if (binary_sensor->has_own_name()) + msg.name = binary_sensor->get_name(); msg.unique_id = get_default_unique_id("binary_sensor", binary_sensor); msg.device_class = binary_sensor->get_device_class(); msg.is_status_binary_sensor = binary_sensor->is_status_binary_sensor(); @@ -212,7 +213,8 @@ bool APIConnection::send_cover_info(cover::Cover *cover) { ListEntitiesCoverResponse msg; msg.key = cover->get_object_id_hash(); msg.object_id = cover->get_object_id(); - msg.name = cover->get_name(); + if (cover->has_own_name()) + msg.name = cover->get_name(); msg.unique_id = get_default_unique_id("cover", cover); msg.assumed_state = traits.get_is_assumed_state(); msg.supports_position = traits.get_supports_position(); @@ -275,7 +277,8 @@ bool APIConnection::send_fan_info(fan::Fan *fan) { ListEntitiesFanResponse msg; msg.key = fan->get_object_id_hash(); msg.object_id = fan->get_object_id(); - msg.name = fan->get_name(); + if (fan->has_own_name()) + msg.name = fan->get_name(); msg.unique_id = get_default_unique_id("fan", fan); msg.supports_oscillation = traits.supports_oscillation(); msg.supports_speed = traits.supports_speed(); @@ -337,7 +340,8 @@ bool APIConnection::send_light_info(light::LightState *light) { ListEntitiesLightResponse msg; msg.key = light->get_object_id_hash(); msg.object_id = light->get_object_id(); - msg.name = light->get_name(); + if (light->has_own_name()) + msg.name = light->get_name(); msg.unique_id = get_default_unique_id("light", light); msg.disabled_by_default = light->is_disabled_by_default(); @@ -418,7 +422,8 @@ bool APIConnection::send_sensor_info(sensor::Sensor *sensor) { ListEntitiesSensorResponse msg; msg.key = sensor->get_object_id_hash(); msg.object_id = sensor->get_object_id(); - msg.name = sensor->get_name(); + if (sensor->has_own_name()) + msg.name = sensor->get_name(); msg.unique_id = sensor->unique_id(); if (msg.unique_id.empty()) msg.unique_id = get_default_unique_id("sensor", sensor); @@ -448,7 +453,8 @@ bool APIConnection::send_switch_info(switch_::Switch *a_switch) { ListEntitiesSwitchResponse msg; msg.key = a_switch->get_object_id_hash(); msg.object_id = a_switch->get_object_id(); - msg.name = a_switch->get_name(); + if (a_switch->has_own_name()) + msg.name = a_switch->get_name(); msg.unique_id = get_default_unique_id("switch", a_switch); msg.icon = a_switch->get_icon(); msg.assumed_state = a_switch->assumed_state(); @@ -533,7 +539,8 @@ bool APIConnection::send_climate_info(climate::Climate *climate) { ListEntitiesClimateResponse msg; msg.key = climate->get_object_id_hash(); msg.object_id = climate->get_object_id(); - msg.name = climate->get_name(); + if (climate->has_own_name()) + msg.name = climate->get_name(); msg.unique_id = get_default_unique_id("climate", climate); msg.disabled_by_default = climate->is_disabled_by_default(); @@ -611,7 +618,8 @@ bool APIConnection::send_number_info(number::Number *number) { ListEntitiesNumberResponse msg; msg.key = number->get_object_id_hash(); msg.object_id = number->get_object_id(); - msg.name = number->get_name(); + if (number->has_own_name()) + msg.name = number->get_name(); msg.unique_id = get_default_unique_id("number", number); msg.icon = number->get_icon(); msg.disabled_by_default = number->is_disabled_by_default(); @@ -652,7 +660,8 @@ bool APIConnection::send_select_info(select::Select *select) { ListEntitiesSelectResponse msg; msg.key = select->get_object_id_hash(); msg.object_id = select->get_object_id(); - msg.name = select->get_name(); + if (select->has_own_name()) + msg.name = select->get_name(); msg.unique_id = get_default_unique_id("select", select); msg.icon = select->get_icon(); msg.disabled_by_default = select->is_disabled_by_default(); @@ -679,7 +688,8 @@ bool APIConnection::send_button_info(button::Button *button) { ListEntitiesButtonResponse msg; msg.key = button->get_object_id_hash(); msg.object_id = button->get_object_id(); - msg.name = button->get_name(); + if (button->has_own_name()) + msg.name = button->get_name(); msg.unique_id = get_default_unique_id("button", button); msg.icon = button->get_icon(); msg.disabled_by_default = button->is_disabled_by_default(); @@ -710,7 +720,8 @@ bool APIConnection::send_lock_info(lock::Lock *a_lock) { ListEntitiesLockResponse msg; msg.key = a_lock->get_object_id_hash(); msg.object_id = a_lock->get_object_id(); - msg.name = a_lock->get_name(); + if (a_lock->has_own_name()) + msg.name = a_lock->get_name(); msg.unique_id = get_default_unique_id("lock", a_lock); msg.icon = a_lock->get_icon(); msg.assumed_state = a_lock->traits.get_assumed_state(); @@ -755,7 +766,8 @@ bool APIConnection::send_media_player_info(media_player::MediaPlayer *media_play ListEntitiesMediaPlayerResponse msg; msg.key = media_player->get_object_id_hash(); msg.object_id = media_player->get_object_id(); - msg.name = media_player->get_name(); + if (media_player->has_own_name()) + msg.name = media_player->get_name(); msg.unique_id = get_default_unique_id("media_player", media_player); msg.icon = media_player->get_icon(); msg.disabled_by_default = media_player->is_disabled_by_default(); @@ -799,7 +811,8 @@ bool APIConnection::send_camera_info(esp32_camera::ESP32Camera *camera) { ListEntitiesCameraResponse msg; msg.key = camera->get_object_id_hash(); msg.object_id = camera->get_object_id(); - msg.name = camera->get_name(); + if (camera->has_own_name()) + msg.name = camera->get_name(); msg.unique_id = get_default_unique_id("camera", camera); msg.disabled_by_default = camera->is_disabled_by_default(); msg.icon = camera->get_icon(); diff --git a/esphome/components/esp32_improv/__init__.py b/esphome/components/esp32_improv/__init__.py index 7170a6dabf..ae7f0b6427 100644 --- a/esphome/components/esp32_improv/__init__.py +++ b/esphome/components/esp32_improv/__init__.py @@ -22,20 +22,12 @@ ESP32ImprovComponent = esp32_improv_ns.class_( ) -def validate_none_(value): - if value in ("none", "None"): - return None - if cv.boolean(value) is False: - return None - raise cv.Invalid("Must be none") - - CONFIG_SCHEMA = cv.Schema( { cv.GenerateID(): cv.declare_id(ESP32ImprovComponent), cv.GenerateID(CONF_BLE_SERVER_ID): cv.use_id(esp32_ble_server.BLEServer), cv.Required(CONF_AUTHORIZER): cv.Any( - validate_none_, cv.use_id(binary_sensor.BinarySensor) + cv.none, cv.use_id(binary_sensor.BinarySensor) ), cv.Optional(CONF_STATUS_INDICATOR): cv.use_id(output.BinaryOutput), cv.Optional( diff --git a/esphome/config_validation.py b/esphome/config_validation.py index f0bbc368b8..4b822b46c9 100644 --- a/esphome/config_validation.py +++ b/esphome/config_validation.py @@ -1514,6 +1514,8 @@ def _entity_base_validator(config): config[CONF_NAME] = id.id config[CONF_INTERNAL] = True return config + if config[CONF_NAME] is None: + config[CONF_NAME] = "" return config @@ -1573,6 +1575,23 @@ def validate_registry_entry(name, registry): return validator +def none(value): + if value in ("none", "None"): + return None + if boolean(value) is False: + return None + raise Invalid("Must be none") + + +def requires_friendly_name(message): + def validate(value): + if CORE.friendly_name is None: + raise Invalid(message) + return value + + return validate + + def validate_registry(name, registry): return ensure_list(validate_registry_entry(name, registry)) @@ -1632,7 +1651,15 @@ MQTT_COMMAND_COMPONENT_SCHEMA = MQTT_COMPONENT_SCHEMA.extend( ENTITY_BASE_SCHEMA = Schema( { - Optional(CONF_NAME): string, + Optional(CONF_NAME): Any( + All( + none, + requires_friendly_name( + "Name cannot be None when esphome->friendly_name is not set!" + ), + ), + string, + ), Optional(CONF_INTERNAL): boolean, Optional(CONF_DISABLED_BY_DEFAULT, default=False): boolean, Optional(CONF_ICON): icon, diff --git a/esphome/core/entity_base.cpp b/esphome/core/entity_base.cpp index 6f88f069b3..49a73854a1 100644 --- a/esphome/core/entity_base.cpp +++ b/esphome/core/entity_base.cpp @@ -1,4 +1,5 @@ #include "esphome/core/entity_base.h" +#include "esphome/core/application.h" #include "esphome/core/helpers.h" namespace esphome { @@ -10,7 +11,13 @@ EntityBase::EntityBase(std::string name) : name_(std::move(name)) { this->calc_o // Entity Name const std::string &EntityBase::get_name() const { return this->name_; } void EntityBase::set_name(const std::string &name) { - this->name_ = name; + if (name.empty()) { + this->name_ = App.get_friendly_name(); + this->has_own_name_ = false; + } else { + this->name_ = name; + this->has_own_name_ = true; + } this->calc_object_id_(); } diff --git a/esphome/core/entity_base.h b/esphome/core/entity_base.h index f6aae4e978..5ab1f7b424 100644 --- a/esphome/core/entity_base.h +++ b/esphome/core/entity_base.h @@ -21,6 +21,9 @@ class EntityBase { const std::string &get_name() const; void set_name(const std::string &name); + // Get whether this Entity has its own name or it should use the device friendly_name. + bool has_own_name() const { return this->has_own_name_; } + // Get the sanitized name of this Entity as an ID. Caching it internally. const std::string &get_object_id(); @@ -52,6 +55,7 @@ class EntityBase { void calc_object_id_(); std::string name_; + bool has_own_name_{false}; std::string object_id_; const char *icon_c_str_{nullptr}; uint32_t object_id_hash_; From 06f83bf1c00a7c07f1fd1981bfca7a2663b4596c Mon Sep 17 00:00:00 2001 From: Jesse Hills <3060199+jesserockz@users.noreply.github.com> Date: Mon, 27 Mar 2023 11:50:33 +1300 Subject: [PATCH 8/8] Fix platform restriction for bme680_bsec (#4616) * Fix platform restriction for bme680_bsec * Fix * revert spi change * Add comment back * Dont crash on rp2040 platform --- esphome/components/bme680_bsec/__init__.py | 41 +++++++++++++--------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/esphome/components/bme680_bsec/__init__.py b/esphome/components/bme680_bsec/__init__.py index c9813c4974..085d2a574b 100644 --- a/esphome/components/bme680_bsec/__init__.py +++ b/esphome/components/bme680_bsec/__init__.py @@ -1,6 +1,6 @@ import esphome.codegen as cg import esphome.config_validation as cv -from esphome.components import i2c +from esphome.components import i2c, esp32 from esphome.const import CONF_ID CODEOWNERS = ["@trvrnrth"] @@ -32,22 +32,31 @@ BME680BSECComponent = bme680_bsec_ns.class_( "BME680BSECComponent", cg.Component, i2c.I2CDevice ) -CONFIG_SCHEMA = cv.Schema( - { - cv.GenerateID(): cv.declare_id(BME680BSECComponent), - cv.Optional(CONF_TEMPERATURE_OFFSET, default=0): cv.temperature, - cv.Optional(CONF_IAQ_MODE, default="STATIC"): cv.enum( - IAQ_MODE_OPTIONS, upper=True - ), - cv.Optional(CONF_SAMPLE_RATE, default="LP"): cv.enum( - SAMPLE_RATE_OPTIONS, upper=True - ), - cv.Optional( - CONF_STATE_SAVE_INTERVAL, default="6hours" - ): cv.positive_time_period_minutes, - }, +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(BME680BSECComponent), + cv.Optional(CONF_TEMPERATURE_OFFSET, default=0): cv.temperature, + cv.Optional(CONF_IAQ_MODE, default="STATIC"): cv.enum( + IAQ_MODE_OPTIONS, upper=True + ), + cv.Optional(CONF_SAMPLE_RATE, default="LP"): cv.enum( + SAMPLE_RATE_OPTIONS, upper=True + ), + cv.Optional( + CONF_STATE_SAVE_INTERVAL, default="6hours" + ): cv.positive_time_period_minutes, + } + ).extend(i2c.i2c_device_schema(0x76)), cv.only_with_arduino, -).extend(i2c.i2c_device_schema(0x76)) + cv.Any( + cv.only_on_esp8266, + cv.All( + cv.only_on_esp32, + esp32.only_on_variant(supported=[esp32.const.VARIANT_ESP32]), + ), + ), +) async def to_code(config):