From 1d25db491c26b7400a8483ab2199fa8d05badfbc Mon Sep 17 00:00:00 2001 From: Markus <974709+Links2004@users.noreply.github.com> Date: Wed, 14 Aug 2024 04:03:12 +0200 Subject: [PATCH] [homeassistant] Native switch entity import and control (#7018) Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- CODEOWNERS | 1 + .../homeassistant/switch/__init__.py | 30 ++++++++++ .../switch/homeassistant_switch.cpp | 59 +++++++++++++++++++ .../switch/homeassistant_switch.h | 22 +++++++ tests/components/homeassistant/common.yaml | 5 ++ 5 files changed, 117 insertions(+) create mode 100644 esphome/components/homeassistant/switch/__init__.py create mode 100644 esphome/components/homeassistant/switch/homeassistant_switch.cpp create mode 100644 esphome/components/homeassistant/switch/homeassistant_switch.h diff --git a/CODEOWNERS b/CODEOWNERS index 663a942cb4..0c36cda527 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -169,6 +169,7 @@ esphome/components/heatpumpir/* @rob-deutsch esphome/components/hitachi_ac424/* @sourabhjaiswal esphome/components/hm3301/* @freekode esphome/components/homeassistant/* @OttoWinter @esphome/core +esphome/components/homeassistant/switch/* @Links2004 esphome/components/honeywell_hih_i2c/* @Benichou34 esphome/components/honeywellabp/* @RubyBailey esphome/components/honeywellabp2_i2c/* @jpfaff diff --git a/esphome/components/homeassistant/switch/__init__.py b/esphome/components/homeassistant/switch/__init__.py new file mode 100644 index 0000000000..3d7c80682a --- /dev/null +++ b/esphome/components/homeassistant/switch/__init__.py @@ -0,0 +1,30 @@ +import esphome.codegen as cg +from esphome.components import switch +import esphome.config_validation as cv +from esphome.const import CONF_ID + +from .. import ( + HOME_ASSISTANT_IMPORT_CONTROL_SCHEMA, + homeassistant_ns, + setup_home_assistant_entity, +) + +CODEOWNERS = ["@Links2004"] +DEPENDENCIES = ["api"] + +HomeassistantSwitch = homeassistant_ns.class_( + "HomeassistantSwitch", switch.Switch, cg.Component +) + +CONFIG_SCHEMA = ( + switch.switch_schema(HomeassistantSwitch) + .extend(cv.COMPONENT_SCHEMA) + .extend(HOME_ASSISTANT_IMPORT_CONTROL_SCHEMA) +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await switch.register_switch(var, config) + setup_home_assistant_entity(var, config) diff --git a/esphome/components/homeassistant/switch/homeassistant_switch.cpp b/esphome/components/homeassistant/switch/homeassistant_switch.cpp new file mode 100644 index 0000000000..05ef46e30e --- /dev/null +++ b/esphome/components/homeassistant/switch/homeassistant_switch.cpp @@ -0,0 +1,59 @@ +#include "homeassistant_switch.h" +#include "esphome/components/api/api_server.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace homeassistant { + +static const char *const TAG = "homeassistant.switch"; + +using namespace esphome::switch_; + +void HomeassistantSwitch::setup() { + api::global_api_server->subscribe_home_assistant_state(this->entity_id_, nullopt, [this](const std::string &state) { + auto val = parse_on_off(state.c_str()); + switch (val) { + case PARSE_NONE: + case PARSE_TOGGLE: + ESP_LOGW(TAG, "Can't convert '%s' to binary state!", state.c_str()); + break; + case PARSE_ON: + case PARSE_OFF: + bool new_state = val == PARSE_ON; + ESP_LOGD(TAG, "'%s': Got state %s", this->entity_id_.c_str(), ONOFF(new_state)); + this->publish_state(new_state); + break; + } + }); +} + +void HomeassistantSwitch::dump_config() { + LOG_SWITCH("", "Homeassistant Switch", this); + ESP_LOGCONFIG(TAG, " Entity ID: '%s'", this->entity_id_.c_str()); +} + +float HomeassistantSwitch::get_setup_priority() const { return setup_priority::AFTER_CONNECTION; } + +void HomeassistantSwitch::write_state(bool state) { + if (!api::global_api_server->is_connected()) { + ESP_LOGE(TAG, "No clients connected to API server"); + return; + } + + api::HomeassistantServiceResponse resp; + if (state) { + resp.service = "switch.turn_on"; + } else { + resp.service = "switch.turn_off"; + } + + api::HomeassistantServiceMap entity_id_kv; + entity_id_kv.key = "entity_id"; + entity_id_kv.value = this->entity_id_; + resp.data.push_back(entity_id_kv); + + api::global_api_server->send_homeassistant_service_call(resp); +} + +} // namespace homeassistant +} // namespace esphome diff --git a/esphome/components/homeassistant/switch/homeassistant_switch.h b/esphome/components/homeassistant/switch/homeassistant_switch.h new file mode 100644 index 0000000000..a4da257960 --- /dev/null +++ b/esphome/components/homeassistant/switch/homeassistant_switch.h @@ -0,0 +1,22 @@ +#pragma once + +#include "esphome/components/switch/switch.h" +#include "esphome/core/component.h" + +namespace esphome { +namespace homeassistant { + +class HomeassistantSwitch : public switch_::Switch, public Component { + public: + void set_entity_id(const std::string &entity_id) { this->entity_id_ = entity_id; } + void setup() override; + void dump_config() override; + float get_setup_priority() const override; + + protected: + void write_state(bool state) override; + std::string entity_id_; +}; + +} // namespace homeassistant +} // namespace esphome diff --git a/tests/components/homeassistant/common.yaml b/tests/components/homeassistant/common.yaml index 07a6e8090c..2b2805d06e 100644 --- a/tests/components/homeassistant/common.yaml +++ b/tests/components/homeassistant/common.yaml @@ -32,6 +32,11 @@ wifi: api: +switch: + - platform: homeassistant + entity_id: switch.my_cool_switch + id: my_cool_switch + binary_sensor: - platform: homeassistant entity_id: binary_sensor.hello_world