mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	Anova ble component (#1752)
Co-authored-by: Ben Buxton <bb@cactii.net> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
		| @@ -15,6 +15,7 @@ esphome/components/ac_dimmer/* @glmnet | |||||||
| esphome/components/adc/* @esphome/core | esphome/components/adc/* @esphome/core | ||||||
| esphome/components/addressable_light/* @justfalter | esphome/components/addressable_light/* @justfalter | ||||||
| esphome/components/animation/* @syndlex | esphome/components/animation/* @syndlex | ||||||
|  | esphome/components/anova/* @buxtronix | ||||||
| esphome/components/api/* @OttoWinter | esphome/components/api/* @OttoWinter | ||||||
| esphome/components/async_tcp/* @OttoWinter | esphome/components/async_tcp/* @OttoWinter | ||||||
| esphome/components/atc_mithermometer/* @ahpohl | esphome/components/atc_mithermometer/* @ahpohl | ||||||
|   | |||||||
							
								
								
									
										0
									
								
								esphome/components/anova/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								esphome/components/anova/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										140
									
								
								esphome/components/anova/anova.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								esphome/components/anova/anova.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,140 @@ | |||||||
|  | #include "anova.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | #ifdef ARDUINO_ARCH_ESP32 | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace anova { | ||||||
|  |  | ||||||
|  | static const char *TAG = "anova"; | ||||||
|  |  | ||||||
|  | using namespace esphome::climate; | ||||||
|  |  | ||||||
|  | void Anova::dump_config() { LOG_CLIMATE("", "Anova BLE Cooker", this); } | ||||||
|  |  | ||||||
|  | void Anova::setup() { | ||||||
|  |   this->codec_ = new AnovaCodec(); | ||||||
|  |   this->current_request_ = 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Anova::loop() {} | ||||||
|  |  | ||||||
|  | void Anova::control(const ClimateCall &call) { | ||||||
|  |   if (call.get_mode().has_value()) { | ||||||
|  |     ClimateMode mode = *call.get_mode(); | ||||||
|  |     AnovaPacket *pkt; | ||||||
|  |     switch (mode) { | ||||||
|  |       case climate::CLIMATE_MODE_OFF: | ||||||
|  |         pkt = this->codec_->get_stop_request(); | ||||||
|  |         break; | ||||||
|  |       case climate::CLIMATE_MODE_HEAT: | ||||||
|  |         pkt = this->codec_->get_start_request(); | ||||||
|  |         break; | ||||||
|  |       default: | ||||||
|  |         ESP_LOGW(TAG, "Unsupported mode: %d", mode); | ||||||
|  |         return; | ||||||
|  |     } | ||||||
|  |     auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, | ||||||
|  |                                            pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); | ||||||
|  |     if (status) | ||||||
|  |       ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status); | ||||||
|  |   } | ||||||
|  |   if (call.get_target_temperature().has_value()) { | ||||||
|  |     auto pkt = this->codec_->get_set_target_temp_request(*call.get_target_temperature()); | ||||||
|  |     auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, | ||||||
|  |                                            pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); | ||||||
|  |     if (status) | ||||||
|  |       ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Anova::gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param) { | ||||||
|  |   switch (event) { | ||||||
|  |     case ESP_GATTC_DISCONNECT_EVT: { | ||||||
|  |       this->current_temperature = NAN; | ||||||
|  |       this->target_temperature = NAN; | ||||||
|  |       this->publish_state(); | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     case ESP_GATTC_SEARCH_CMPL_EVT: { | ||||||
|  |       auto chr = this->parent_->get_characteristic(ANOVA_SERVICE_UUID, ANOVA_CHARACTERISTIC_UUID); | ||||||
|  |       if (chr == nullptr) { | ||||||
|  |         ESP_LOGW(TAG, "[%s] No control service found at device, not an Anova..?", this->get_name().c_str()); | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |       this->char_handle_ = chr->handle; | ||||||
|  |  | ||||||
|  |       auto status = esp_ble_gattc_register_for_notify(this->parent_->gattc_if, this->parent_->remote_bda, chr->handle); | ||||||
|  |       if (status) { | ||||||
|  |         ESP_LOGW(TAG, "[%s] esp_ble_gattc_register_for_notify failed, status=%d", this->get_name().c_str(), status); | ||||||
|  |       } | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     case ESP_GATTC_REG_FOR_NOTIFY_EVT: { | ||||||
|  |       this->node_state = espbt::ClientState::Established; | ||||||
|  |       this->current_request_ = 0; | ||||||
|  |       this->update(); | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     case ESP_GATTC_NOTIFY_EVT: { | ||||||
|  |       if (param->notify.handle != this->char_handle_) | ||||||
|  |         break; | ||||||
|  |       this->codec_->decode(param->notify.value, param->notify.value_len); | ||||||
|  |       if (this->codec_->has_target_temp()) { | ||||||
|  |         this->target_temperature = this->codec_->target_temp_; | ||||||
|  |       } | ||||||
|  |       if (this->codec_->has_current_temp()) { | ||||||
|  |         this->current_temperature = this->codec_->current_temp_; | ||||||
|  |       } | ||||||
|  |       if (this->codec_->has_running()) { | ||||||
|  |         this->mode = this->codec_->running_ ? climate::CLIMATE_MODE_HEAT : climate::CLIMATE_MODE_OFF; | ||||||
|  |       } | ||||||
|  |       this->publish_state(); | ||||||
|  |  | ||||||
|  |       if (this->current_request_ > 0) { | ||||||
|  |         AnovaPacket *pkt = nullptr; | ||||||
|  |         switch (this->current_request_++) { | ||||||
|  |           case 1: | ||||||
|  |             pkt = this->codec_->get_read_target_temp_request(); | ||||||
|  |             break; | ||||||
|  |           case 2: | ||||||
|  |             pkt = this->codec_->get_read_current_temp_request(); | ||||||
|  |             break; | ||||||
|  |           default: | ||||||
|  |             this->current_request_ = 0; | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         if (pkt != nullptr) { | ||||||
|  |           auto status = | ||||||
|  |               esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, pkt->length, | ||||||
|  |                                        pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); | ||||||
|  |           if (status) | ||||||
|  |             ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), | ||||||
|  |                      status); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     default: | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void Anova::update() { | ||||||
|  |   if (this->node_state != espbt::ClientState::Established) | ||||||
|  |     return; | ||||||
|  |  | ||||||
|  |   if (this->current_request_ == 0) { | ||||||
|  |     auto pkt = this->codec_->get_read_device_status_request(); | ||||||
|  |     auto status = esp_ble_gattc_write_char(this->parent_->gattc_if, this->parent_->conn_id, this->char_handle_, | ||||||
|  |                                            pkt->length, pkt->data, ESP_GATT_WRITE_TYPE_NO_RSP, ESP_GATT_AUTH_REQ_NONE); | ||||||
|  |     if (status) | ||||||
|  |       ESP_LOGW(TAG, "[%s] esp_ble_gattc_write_char failed, status=%d", this->parent_->address_str().c_str(), status); | ||||||
|  |     this->current_request_++; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace anova | ||||||
|  | }  // namespace esphome | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										50
									
								
								esphome/components/anova/anova.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								esphome/components/anova/anova.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/components/ble_client/ble_client.h" | ||||||
|  | #include "esphome/components/esp32_ble_tracker/esp32_ble_tracker.h" | ||||||
|  | #include "esphome/components/climate/climate.h" | ||||||
|  | #include "anova_base.h" | ||||||
|  |  | ||||||
|  | #ifdef ARDUINO_ARCH_ESP32 | ||||||
|  |  | ||||||
|  | #include <esp_gattc_api.h> | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace anova { | ||||||
|  |  | ||||||
|  | namespace espbt = esphome::esp32_ble_tracker; | ||||||
|  |  | ||||||
|  | static const uint16_t ANOVA_SERVICE_UUID = 0xFFE0; | ||||||
|  | static const uint16_t ANOVA_CHARACTERISTIC_UUID = 0xFFE1; | ||||||
|  |  | ||||||
|  | class Anova : public climate::Climate, public esphome::ble_client::BLEClientNode, public PollingComponent { | ||||||
|  |  public: | ||||||
|  |   void setup() override; | ||||||
|  |   void loop() override; | ||||||
|  |   void update() override; | ||||||
|  |   void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, | ||||||
|  |                            esp_ble_gattc_cb_param_t *param) override; | ||||||
|  |   void dump_config() override; | ||||||
|  |   float get_setup_priority() const override { return setup_priority::DATA; } | ||||||
|  |   climate::ClimateTraits traits() { | ||||||
|  |     auto traits = climate::ClimateTraits(); | ||||||
|  |     traits.set_supports_current_temperature(true); | ||||||
|  |     traits.set_supports_heat_mode(true); | ||||||
|  |     traits.set_visual_min_temperature(25.0); | ||||||
|  |     traits.set_visual_max_temperature(100.0); | ||||||
|  |     traits.set_visual_temperature_step(0.1); | ||||||
|  |     return traits; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   AnovaCodec *codec_; | ||||||
|  |   void control(const climate::ClimateCall &call) override; | ||||||
|  |   uint16_t char_handle_; | ||||||
|  |   uint8_t current_request_; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace anova | ||||||
|  | }  // namespace esphome | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										119
									
								
								esphome/components/anova/anova_base.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								esphome/components/anova/anova_base.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,119 @@ | |||||||
|  | #include "anova_base.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace anova { | ||||||
|  |  | ||||||
|  | AnovaPacket *AnovaCodec::clean_packet_() { | ||||||
|  |   this->packet_.length = strlen((char *) this->packet_.data); | ||||||
|  |   this->packet_.data[this->packet_.length] = '\0'; | ||||||
|  |   ESP_LOGV("anova", "SendPkt: %s\n", this->packet_.data); | ||||||
|  |   return &this->packet_; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | AnovaPacket *AnovaCodec::get_read_device_status_request() { | ||||||
|  |   this->current_query_ = READ_DEVICE_STATUS; | ||||||
|  |   sprintf((char *) this->packet_.data, "%s", CMD_READ_DEVICE_STATUS); | ||||||
|  |   return this->clean_packet_(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | AnovaPacket *AnovaCodec::get_read_target_temp_request() { | ||||||
|  |   this->current_query_ = READ_TARGET_TEMPERATURE; | ||||||
|  |   sprintf((char *) this->packet_.data, "%s", CMD_READ_TARGET_TEMP); | ||||||
|  |   return this->clean_packet_(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | AnovaPacket *AnovaCodec::get_read_current_temp_request() { | ||||||
|  |   this->current_query_ = READ_CURRENT_TEMPERATURE; | ||||||
|  |   sprintf((char *) this->packet_.data, "%s", CMD_READ_CURRENT_TEMP); | ||||||
|  |   return this->clean_packet_(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | AnovaPacket *AnovaCodec::get_read_unit_request() { | ||||||
|  |   this->current_query_ = READ_UNIT; | ||||||
|  |   sprintf((char *) this->packet_.data, "%s", CMD_READ_UNIT); | ||||||
|  |   return this->clean_packet_(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | AnovaPacket *AnovaCodec::get_read_data_request() { | ||||||
|  |   this->current_query_ = READ_DATA; | ||||||
|  |   sprintf((char *) this->packet_.data, "%s", CMD_READ_DATA); | ||||||
|  |   return this->clean_packet_(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | AnovaPacket *AnovaCodec::get_set_target_temp_request(float temperature) { | ||||||
|  |   this->current_query_ = SET_TARGET_TEMPERATURE; | ||||||
|  |   sprintf((char *) this->packet_.data, CMD_SET_TARGET_TEMP, temperature); | ||||||
|  |   return this->clean_packet_(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | AnovaPacket *AnovaCodec::get_set_unit_request(char unit) { | ||||||
|  |   this->current_query_ = SET_UNIT; | ||||||
|  |   sprintf((char *) this->packet_.data, CMD_SET_TEMP_UNIT, unit); | ||||||
|  |   return this->clean_packet_(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | AnovaPacket *AnovaCodec::get_start_request() { | ||||||
|  |   this->current_query_ = START; | ||||||
|  |   sprintf((char *) this->packet_.data, CMD_START); | ||||||
|  |   return this->clean_packet_(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | AnovaPacket *AnovaCodec::get_stop_request() { | ||||||
|  |   this->current_query_ = STOP; | ||||||
|  |   sprintf((char *) this->packet_.data, CMD_STOP); | ||||||
|  |   return this->clean_packet_(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void AnovaCodec::decode(const uint8_t *data, uint16_t length) { | ||||||
|  |   memset(this->buf_, 0, 32); | ||||||
|  |   strncpy(this->buf_, (char *) data, length); | ||||||
|  |   ESP_LOGV("anova", "Received: %s\n", this->buf_); | ||||||
|  |   this->has_target_temp_ = this->has_current_temp_ = this->has_unit_ = this->has_running_ = false; | ||||||
|  |   switch (this->current_query_) { | ||||||
|  |     case READ_DEVICE_STATUS: { | ||||||
|  |       if (!strncmp(this->buf_, "stopped", 7)) { | ||||||
|  |         this->has_running_ = true; | ||||||
|  |         this->running_ = false; | ||||||
|  |       } | ||||||
|  |       if (!strncmp(this->buf_, "running", 7)) { | ||||||
|  |         this->has_running_ = true; | ||||||
|  |         this->running_ = true; | ||||||
|  |       } | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     case START: { | ||||||
|  |       if (!strncmp(this->buf_, "start", 5)) { | ||||||
|  |         this->has_running_ = true; | ||||||
|  |         this->running_ = true; | ||||||
|  |       } | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     case STOP: { | ||||||
|  |       if (!strncmp(this->buf_, "stop", 4)) { | ||||||
|  |         this->has_running_ = true; | ||||||
|  |         this->running_ = false; | ||||||
|  |       } | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     case READ_TARGET_TEMPERATURE: { | ||||||
|  |       this->target_temp_ = strtof(this->buf_, nullptr); | ||||||
|  |       this->has_target_temp_ = true; | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     case SET_TARGET_TEMPERATURE: { | ||||||
|  |       this->target_temp_ = strtof(this->buf_, nullptr); | ||||||
|  |       this->has_target_temp_ = true; | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     case READ_CURRENT_TEMPERATURE: { | ||||||
|  |       this->current_temp_ = strtof(this->buf_, nullptr); | ||||||
|  |       this->has_current_temp_ = true; | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     default: | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace anova | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										79
									
								
								esphome/components/anova/anova_base.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								esphome/components/anova/anova_base.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/helpers.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace anova { | ||||||
|  |  | ||||||
|  | enum CurrentQuery { | ||||||
|  |   NONE, | ||||||
|  |   READ_DEVICE_STATUS, | ||||||
|  |   READ_TARGET_TEMPERATURE, | ||||||
|  |   READ_CURRENT_TEMPERATURE, | ||||||
|  |   READ_DATA, | ||||||
|  |   READ_UNIT, | ||||||
|  |   SET_TARGET_TEMPERATURE, | ||||||
|  |   SET_UNIT, | ||||||
|  |   START, | ||||||
|  |   STOP, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | struct AnovaPacket { | ||||||
|  |   uint16_t length; | ||||||
|  |   uint8_t data[24]; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | #define CMD_READ_DEVICE_STATUS "status\r" | ||||||
|  | #define CMD_READ_TARGET_TEMP "read set temp\r" | ||||||
|  | #define CMD_READ_CURRENT_TEMP "read temp\r" | ||||||
|  | #define CMD_READ_UNIT "read unit\r" | ||||||
|  | #define CMD_READ_DATA "read data\r" | ||||||
|  | #define CMD_SET_TARGET_TEMP "set temp %.1f\r" | ||||||
|  | #define CMD_SET_TEMP_UNIT "set unit %c\r" | ||||||
|  |  | ||||||
|  | #define CMD_START "start\r" | ||||||
|  | #define CMD_STOP "stop\r" | ||||||
|  |  | ||||||
|  | class AnovaCodec { | ||||||
|  |  public: | ||||||
|  |   AnovaPacket *get_read_device_status_request(); | ||||||
|  |   AnovaPacket *get_read_target_temp_request(); | ||||||
|  |   AnovaPacket *get_read_current_temp_request(); | ||||||
|  |   AnovaPacket *get_read_data_request(); | ||||||
|  |   AnovaPacket *get_read_unit_request(); | ||||||
|  |  | ||||||
|  |   AnovaPacket *get_set_target_temp_request(float temperature); | ||||||
|  |   AnovaPacket *get_set_unit_request(char unit); | ||||||
|  |  | ||||||
|  |   AnovaPacket *get_start_request(); | ||||||
|  |   AnovaPacket *get_stop_request(); | ||||||
|  |  | ||||||
|  |   void decode(const uint8_t *data, uint16_t length); | ||||||
|  |   bool has_target_temp() { return this->has_target_temp_; } | ||||||
|  |   bool has_current_temp() { return this->has_current_temp_; } | ||||||
|  |   bool has_unit() { return this->has_unit_; } | ||||||
|  |   bool has_running() { return this->has_running_; } | ||||||
|  |  | ||||||
|  |   union { | ||||||
|  |     float target_temp_; | ||||||
|  |     float current_temp_; | ||||||
|  |     char unit_; | ||||||
|  |     bool running_; | ||||||
|  |   }; | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   AnovaPacket *clean_packet_(); | ||||||
|  |   AnovaPacket packet_; | ||||||
|  |  | ||||||
|  |   bool has_target_temp_; | ||||||
|  |   bool has_current_temp_; | ||||||
|  |   bool has_unit_; | ||||||
|  |   bool has_running_; | ||||||
|  |   char buf_[32]; | ||||||
|  |  | ||||||
|  |   CurrentQuery current_query_; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace anova | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										25
									
								
								esphome/components/anova/climate.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								esphome/components/anova/climate.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import climate, ble_client | ||||||
|  | from esphome.const import CONF_ID | ||||||
|  |  | ||||||
|  | CODEOWNERS = ["@buxtronix"] | ||||||
|  | DEPENDENCIES = ["ble_client"] | ||||||
|  |  | ||||||
|  | anova_ns = cg.esphome_ns.namespace("anova") | ||||||
|  | Anova = anova_ns.class_( | ||||||
|  |     "Anova", climate.Climate, ble_client.BLEClientNode, cg.PollingComponent | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = ( | ||||||
|  |     climate.CLIMATE_SCHEMA.extend({cv.GenerateID(): cv.declare_id(Anova)}) | ||||||
|  |     .extend(ble_client.BLE_CLIENT_SCHEMA) | ||||||
|  |     .extend(cv.polling_component_schema("60s")) | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def to_code(config): | ||||||
|  |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|  |     yield cg.register_component(var, config) | ||||||
|  |     yield climate.register_climate(var, config) | ||||||
|  |     yield ble_client.register_ble_node(var, config) | ||||||
| @@ -1526,6 +1526,26 @@ climate: | |||||||
|     name: Toshiba Climate |     name: Toshiba Climate | ||||||
|   - platform: hitachi_ac344 |   - platform: hitachi_ac344 | ||||||
|     name: Hitachi Climate |     name: Hitachi Climate | ||||||
|  |   - platform: midea_ac | ||||||
|  |     visual: | ||||||
|  |       min_temperature: 18 °C | ||||||
|  |       max_temperature: 25 °C | ||||||
|  |       temperature_step: 0.1 °C | ||||||
|  |     name: 'Electrolux EACS' | ||||||
|  |     beeper: true | ||||||
|  |     outdoor_temperature: | ||||||
|  |       name: 'Temp' | ||||||
|  |     power_usage: | ||||||
|  |       name: 'Power' | ||||||
|  |     humidity_setpoint: | ||||||
|  |       name: 'Hum' | ||||||
|  |   - platform: anova | ||||||
|  |     name: Anova cooker | ||||||
|  |     ble_client_id: ble_blah | ||||||
|  |  | ||||||
|  | midea_dongle: | ||||||
|  |   uart_id: uart0 | ||||||
|  |   strength_icon: true | ||||||
|  |  | ||||||
| switch: | switch: | ||||||
|   - platform: gpio |   - platform: gpio | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user