mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	Add BedJet Fan child component (#3735)
This commit is contained in:
		| @@ -29,6 +29,8 @@ esphome/components/b_parasite/* @rbaron | |||||||
| esphome/components/ballu/* @bazuchan | esphome/components/ballu/* @bazuchan | ||||||
| esphome/components/bang_bang/* @OttoWinter | esphome/components/bang_bang/* @OttoWinter | ||||||
| esphome/components/bedjet/* @jhansche | esphome/components/bedjet/* @jhansche | ||||||
|  | esphome/components/bedjet/climate/* @jhansche | ||||||
|  | esphome/components/bedjet/fan/* @jhansche | ||||||
| esphome/components/bh1750/* @OttoWinter | esphome/components/bh1750/* @OttoWinter | ||||||
| esphome/components/binary_sensor/* @esphome/core | esphome/components/binary_sensor/* @esphome/core | ||||||
| esphome/components/bl0939/* @ziceva | esphome/components/bl0939/* @ziceva | ||||||
|   | |||||||
| @@ -89,8 +89,10 @@ enum BedjetCommand : uint8_t { | |||||||
|         "85%", "90%", "95%", "100%" \ |         "85%", "90%", "95%", "100%" \ | ||||||
|   } |   } | ||||||
|  |  | ||||||
| static const char *const BEDJET_FAN_STEP_NAMES[20] = BEDJET_FAN_STEP_NAMES_; | static const uint8_t BEDJET_FAN_SPEED_COUNT = 20; | ||||||
| static const std::string BEDJET_FAN_STEP_NAME_STRINGS[20] = BEDJET_FAN_STEP_NAMES_; |  | ||||||
|  | static const char *const BEDJET_FAN_STEP_NAMES[BEDJET_FAN_SPEED_COUNT] = BEDJET_FAN_STEP_NAMES_; | ||||||
|  | static const std::string BEDJET_FAN_STEP_NAME_STRINGS[BEDJET_FAN_SPEED_COUNT] = BEDJET_FAN_STEP_NAMES_; | ||||||
| static const std::set<std::string> BEDJET_FAN_STEP_NAMES_SET BEDJET_FAN_STEP_NAMES_; | static const std::set<std::string> BEDJET_FAN_STEP_NAMES_SET BEDJET_FAN_STEP_NAMES_; | ||||||
|  |  | ||||||
| }  // namespace bedjet | }  // namespace bedjet | ||||||
|   | |||||||
| @@ -9,19 +9,17 @@ from esphome.const import ( | |||||||
|     CONF_RECEIVE_TIMEOUT, |     CONF_RECEIVE_TIMEOUT, | ||||||
|     CONF_TIME_ID, |     CONF_TIME_ID, | ||||||
| ) | ) | ||||||
| from . import ( | from .. import ( | ||||||
|     BEDJET_CLIENT_SCHEMA, |     BEDJET_CLIENT_SCHEMA, | ||||||
|  |     bedjet_ns, | ||||||
|     register_bedjet_child, |     register_bedjet_child, | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| _LOGGER = logging.getLogger(__name__) | _LOGGER = logging.getLogger(__name__) | ||||||
| CODEOWNERS = ["@jhansche"] | CODEOWNERS = ["@jhansche"] | ||||||
| DEPENDENCIES = ["ble_client"] | DEPENDENCIES = ["bedjet"] | ||||||
| 
 | 
 | ||||||
| bedjet_ns = cg.esphome_ns.namespace("bedjet") | BedJetClimate = bedjet_ns.class_("BedJetClimate", climate.Climate, cg.PollingComponent) | ||||||
| BedJetClimate = bedjet_ns.class_( |  | ||||||
|     "BedJetClimate", climate.Climate, ble_client.BLEClientNode, cg.PollingComponent |  | ||||||
| ) |  | ||||||
| BedjetHeatMode = bedjet_ns.enum("BedjetHeatMode") | BedjetHeatMode = bedjet_ns.enum("BedjetHeatMode") | ||||||
| BEDJET_HEAT_MODES = { | BEDJET_HEAT_MODES = { | ||||||
|     "heat": BedjetHeatMode.HEAT_MODE_HEAT, |     "heat": BedjetHeatMode.HEAT_MODE_HEAT, | ||||||
| @@ -15,13 +15,13 @@ float bedjet_temp_to_c(const uint8_t temp) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static const std::string *bedjet_fan_step_to_fan_mode(const uint8_t fan_step) { | static const std::string *bedjet_fan_step_to_fan_mode(const uint8_t fan_step) { | ||||||
|   if (fan_step <= 19) |   if (fan_step < BEDJET_FAN_SPEED_COUNT) | ||||||
|     return &BEDJET_FAN_STEP_NAME_STRINGS[fan_step]; |     return &BEDJET_FAN_STEP_NAME_STRINGS[fan_step]; | ||||||
|   return nullptr; |   return nullptr; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static uint8_t bedjet_fan_speed_to_step(const std::string &fan_step_percent) { | static uint8_t bedjet_fan_speed_to_step(const std::string &fan_step_percent) { | ||||||
|   for (int i = 0; i < sizeof(BEDJET_FAN_STEP_NAME_STRINGS); i++) { |   for (int i = 0; i < BEDJET_FAN_SPEED_COUNT; i++) { | ||||||
|     if (fan_step_percent == BEDJET_FAN_STEP_NAME_STRINGS[i]) { |     if (fan_step_percent == BEDJET_FAN_STEP_NAME_STRINGS[i]) { | ||||||
|       return i; |       return i; | ||||||
|     } |     } | ||||||
| @@ -1,12 +1,12 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "esphome/components/climate/climate.h" |  | ||||||
| #include "esphome/core/component.h" | #include "esphome/core/component.h" | ||||||
| #include "esphome/core/defines.h" | #include "esphome/core/defines.h" | ||||||
| #include "esphome/core/hal.h" | #include "esphome/core/hal.h" | ||||||
| #include "bedjet_child.h" | #include "esphome/components/bedjet/bedjet_child.h" | ||||||
| #include "bedjet_codec.h" | #include "esphome/components/bedjet/bedjet_codec.h" | ||||||
| #include "bedjet_hub.h" | #include "esphome/components/bedjet/bedjet_hub.h" | ||||||
|  | #include "esphome/components/climate/climate.h" | ||||||
| 
 | 
 | ||||||
| #ifdef USE_ESP32 | #ifdef USE_ESP32 | ||||||
| 
 | 
 | ||||||
							
								
								
									
										36
									
								
								esphome/components/bedjet/fan/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								esphome/components/bedjet/fan/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | |||||||
|  | import logging | ||||||
|  |  | ||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import fan | ||||||
|  | from esphome.const import ( | ||||||
|  |     CONF_ID, | ||||||
|  | ) | ||||||
|  | from .. import ( | ||||||
|  |     BEDJET_CLIENT_SCHEMA, | ||||||
|  |     bedjet_ns, | ||||||
|  |     register_bedjet_child, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | _LOGGER = logging.getLogger(__name__) | ||||||
|  | CODEOWNERS = ["@jhansche"] | ||||||
|  | DEPENDENCIES = ["bedjet"] | ||||||
|  |  | ||||||
|  | BedJetFan = bedjet_ns.class_("BedJetFan", fan.Fan, cg.PollingComponent) | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = ( | ||||||
|  |     fan.FAN_SCHEMA.extend( | ||||||
|  |         { | ||||||
|  |             cv.GenerateID(): cv.declare_id(BedJetFan), | ||||||
|  |         } | ||||||
|  |     ) | ||||||
|  |     .extend(cv.polling_component_schema("60s")) | ||||||
|  |     .extend(BEDJET_CLIENT_SCHEMA) | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | async def to_code(config): | ||||||
|  |     var = cg.new_Pvariable(config[CONF_ID]) | ||||||
|  |     await cg.register_component(var, config) | ||||||
|  |     await fan.register_fan(var, config) | ||||||
|  |     await register_bedjet_child(var, config) | ||||||
							
								
								
									
										108
									
								
								esphome/components/bedjet/fan/bedjet_fan.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								esphome/components/bedjet/fan/bedjet_fan.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | |||||||
|  | #include "bedjet_fan.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | #ifdef USE_ESP32 | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace bedjet { | ||||||
|  |  | ||||||
|  | using namespace esphome::fan; | ||||||
|  |  | ||||||
|  | void BedJetFan::dump_config() { LOG_FAN("", "BedJet Fan", this); } | ||||||
|  | std::string BedJetFan::describe() { return "BedJet Fan"; } | ||||||
|  |  | ||||||
|  | void BedJetFan::control(const fan::FanCall &call) { | ||||||
|  |   ESP_LOGD(TAG, "Received BedJetFan::control"); | ||||||
|  |   if (!this->parent_->is_connected()) { | ||||||
|  |     ESP_LOGW(TAG, "Not connected, cannot handle control call yet."); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   bool did_change = false; | ||||||
|  |  | ||||||
|  |   if (call.get_state().has_value() && this->state != *call.get_state()) { | ||||||
|  |     // Turning off is easy: | ||||||
|  |     if (this->state && this->parent_->button_off()) { | ||||||
|  |       this->state = false; | ||||||
|  |       this->publish_state(); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     // Turning on, we have to choose a specific mode; for now, use "COOL" mode | ||||||
|  |     // In the future we could configure the mode to use for fan.turn_on. | ||||||
|  |     if (this->parent_->button_cool()) { | ||||||
|  |       this->state = true; | ||||||
|  |       did_change = true; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // ignore speed changes if not on or turning on | ||||||
|  |   if (this->state && call.get_speed().has_value()) { | ||||||
|  |     this->speed = *call.get_speed(); | ||||||
|  |     this->parent_->set_fan_index(this->speed); | ||||||
|  |     did_change = true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (did_change) { | ||||||
|  |     this->publish_state(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void BedJetFan::on_status(const BedjetStatusPacket *data) { | ||||||
|  |   ESP_LOGVV(TAG, "[%s] Handling on_status with data=%p", this->get_name().c_str(), (void *) data); | ||||||
|  |   bool did_change = false; | ||||||
|  |   bool new_state = data->mode != MODE_STANDBY && data->mode != MODE_WAIT; | ||||||
|  |  | ||||||
|  |   if (new_state != this->state) { | ||||||
|  |     this->state = new_state; | ||||||
|  |     did_change = true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (data->fan_step != this->speed) { | ||||||
|  |     this->speed = data->fan_step; | ||||||
|  |     did_change = true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (did_change) { | ||||||
|  |     this->publish_state(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** Attempts to update the fan device from the last received BedjetStatusPacket. | ||||||
|  |  * | ||||||
|  |  * This will be called from #on_status() when the parent dispatches new status packets, | ||||||
|  |  * and from #update() when the polling interval is triggered. | ||||||
|  |  * | ||||||
|  |  * @return `true` if the status has been applied; `false` if there is nothing to apply. | ||||||
|  |  */ | ||||||
|  | bool BedJetFan::update_status_() { | ||||||
|  |   if (!this->parent_->is_connected()) | ||||||
|  |     return false; | ||||||
|  |   if (!this->parent_->has_status()) | ||||||
|  |     return false; | ||||||
|  |  | ||||||
|  |   auto *status = this->parent_->get_status_packet(); | ||||||
|  |  | ||||||
|  |   if (status == nullptr) | ||||||
|  |     return false; | ||||||
|  |  | ||||||
|  |   this->on_status(status); | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void BedJetFan::update() { | ||||||
|  |   ESP_LOGD(TAG, "[%s] update()", this->get_name().c_str()); | ||||||
|  |   // TODO: if the hub component is already polling, do we also need to include polling? | ||||||
|  |   //  We're already going to get on_status() at the hub's polling interval. | ||||||
|  |   auto result = this->update_status_(); | ||||||
|  |   ESP_LOGD(TAG, "[%s] update_status result=%s", this->get_name().c_str(), result ? "true" : "false"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** Resets states to defaults. */ | ||||||
|  | void BedJetFan::reset_state_() { | ||||||
|  |   this->state = false; | ||||||
|  |   this->publish_state(); | ||||||
|  | } | ||||||
|  | }  // namespace bedjet | ||||||
|  | }  // namespace esphome | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										40
									
								
								esphome/components/bedjet/fan/bedjet_fan.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								esphome/components/bedjet/fan/bedjet_fan.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/core/defines.h" | ||||||
|  | #include "esphome/core/hal.h" | ||||||
|  | #include "esphome/components/bedjet/bedjet_child.h" | ||||||
|  | #include "esphome/components/bedjet/bedjet_codec.h" | ||||||
|  | #include "esphome/components/bedjet/bedjet_hub.h" | ||||||
|  | #include "esphome/components/fan/fan.h" | ||||||
|  |  | ||||||
|  | #ifdef USE_ESP32 | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace bedjet { | ||||||
|  |  | ||||||
|  | class BedJetFan : public fan::Fan, public BedJetClient, public PollingComponent { | ||||||
|  |  public: | ||||||
|  |   void update() override; | ||||||
|  |   void dump_config() override; | ||||||
|  |   float get_setup_priority() const override { return setup_priority::AFTER_WIFI; } | ||||||
|  |  | ||||||
|  |   /* BedJetClient status update */ | ||||||
|  |   void on_status(const BedjetStatusPacket *data) override; | ||||||
|  |   void on_bedjet_state(bool is_ready) override{}; | ||||||
|  |   std::string describe() override; | ||||||
|  |  | ||||||
|  |   fan::FanTraits get_traits() override { return fan::FanTraits(false, true, false, BEDJET_FAN_SPEED_COUNT); } | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   void control(const fan::FanCall &call) override; | ||||||
|  |  | ||||||
|  |  private: | ||||||
|  |   void reset_state_(); | ||||||
|  |   bool update_status_(); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace bedjet | ||||||
|  | }  // namespace esphome | ||||||
|  |  | ||||||
|  | #endif | ||||||
| @@ -2219,6 +2219,9 @@ fan: | |||||||
|     on_speed_set: |     on_speed_set: | ||||||
|       then: |       then: | ||||||
|         - logger.log: "Fan speed was changed!" |         - logger.log: "Fan speed was changed!" | ||||||
|  |   - platform: bedjet | ||||||
|  |     name: My Bedjet fan | ||||||
|  |     bedjet_id: my_bedjet_client | ||||||
|   - platform: copy |   - platform: copy | ||||||
|     source_id: fan_speed |     source_id: fan_speed | ||||||
|     name: "Fan Speed Copy" |     name: "Fan Speed Copy" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user