mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 22:53:59 +00:00 
			
		
		
		
	Add H-Bridge switch component (#7421)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
This commit is contained in:
		| @@ -179,6 +179,7 @@ esphome/components/haier/text_sensor/* @paveldn | ||||
| esphome/components/havells_solar/* @sourabhjaiswal | ||||
| esphome/components/hbridge/fan/* @WeekendWarrior | ||||
| esphome/components/hbridge/light/* @DotNetDann | ||||
| esphome/components/hbridge/switch/* @dwmw2 | ||||
| esphome/components/he60r/* @clydebarrow | ||||
| esphome/components/heatpumpir/* @rob-deutsch | ||||
| esphome/components/hitachi_ac424/* @sourabhjaiswal | ||||
|   | ||||
							
								
								
									
										44
									
								
								esphome/components/hbridge/switch/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								esphome/components/hbridge/switch/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| from esphome import pins | ||||
| import esphome.codegen as cg | ||||
| from esphome.components import switch | ||||
| import esphome.config_validation as cv | ||||
| from esphome.const import CONF_OPTIMISTIC, CONF_PULSE_LENGTH, CONF_WAIT_TIME | ||||
|  | ||||
| from .. import hbridge_ns | ||||
|  | ||||
| HBridgeSwitch = hbridge_ns.class_("HBridgeSwitch", switch.Switch, cg.Component) | ||||
|  | ||||
| CODEOWNERS = ["@dwmw2"] | ||||
|  | ||||
| CONF_OFF_PIN = "off_pin" | ||||
| CONF_ON_PIN = "on_pin" | ||||
|  | ||||
| CONFIG_SCHEMA = ( | ||||
|     switch.switch_schema(HBridgeSwitch) | ||||
|     .extend( | ||||
|         { | ||||
|             cv.Required(CONF_ON_PIN): pins.gpio_output_pin_schema, | ||||
|             cv.Required(CONF_OFF_PIN): pins.gpio_output_pin_schema, | ||||
|             cv.Optional( | ||||
|                 CONF_PULSE_LENGTH, default="100ms" | ||||
|             ): cv.positive_time_period_milliseconds, | ||||
|             cv.Optional(CONF_WAIT_TIME): cv.positive_time_period_milliseconds, | ||||
|             cv.Optional(CONF_OPTIMISTIC, default=False): cv.boolean, | ||||
|         } | ||||
|     ) | ||||
|     .extend(cv.COMPONENT_SCHEMA) | ||||
| ) | ||||
|  | ||||
|  | ||||
| async def to_code(config): | ||||
|     var = await switch.new_switch(config) | ||||
|     await cg.register_component(var, config) | ||||
|  | ||||
|     on_pin = await cg.gpio_pin_expression(config[CONF_ON_PIN]) | ||||
|     cg.add(var.set_on_pin(on_pin)) | ||||
|     off_pin = await cg.gpio_pin_expression(config[CONF_OFF_PIN]) | ||||
|     cg.add(var.set_off_pin(off_pin)) | ||||
|     cg.add(var.set_pulse_length(config[CONF_PULSE_LENGTH])) | ||||
|     cg.add(var.set_optimistic(config[CONF_OPTIMISTIC])) | ||||
|     if wait_time := config.get(CONF_WAIT_TIME): | ||||
|         cg.add(var.set_wait_time(wait_time)) | ||||
							
								
								
									
										95
									
								
								esphome/components/hbridge/switch/hbridge_switch.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								esphome/components/hbridge/switch/hbridge_switch.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,95 @@ | ||||
| #include "hbridge_switch.h" | ||||
| #include "esphome/core/log.h" | ||||
|  | ||||
| #include <cinttypes> | ||||
|  | ||||
| namespace esphome { | ||||
| namespace hbridge { | ||||
|  | ||||
| static const char *const TAG = "switch.hbridge"; | ||||
|  | ||||
| float HBridgeSwitch::get_setup_priority() const { return setup_priority::HARDWARE; } | ||||
| void HBridgeSwitch::setup() { | ||||
|   ESP_LOGCONFIG(TAG, "Setting up H-Bridge Switch '%s'...", this->name_.c_str()); | ||||
|  | ||||
|   optional<bool> initial_state = this->get_initial_state_with_restore_mode().value_or(false); | ||||
|  | ||||
|   // Like GPIOSwitch does, set the pin state both before and after pin setup() | ||||
|   this->on_pin_->digital_write(false); | ||||
|   this->on_pin_->setup(); | ||||
|   this->on_pin_->digital_write(false); | ||||
|  | ||||
|   this->off_pin_->digital_write(false); | ||||
|   this->off_pin_->setup(); | ||||
|   this->off_pin_->digital_write(false); | ||||
|  | ||||
|   if (initial_state.has_value()) | ||||
|     this->write_state(initial_state); | ||||
| } | ||||
|  | ||||
| void HBridgeSwitch::dump_config() { | ||||
|   LOG_SWITCH("", "H-Bridge Switch", this); | ||||
|   LOG_PIN("  On Pin: ", this->on_pin_); | ||||
|   LOG_PIN("  Off Pin: ", this->off_pin_); | ||||
|   ESP_LOGCONFIG(TAG, "  Pulse length: %" PRId32 " ms", this->pulse_length_); | ||||
|   if (this->wait_time_) | ||||
|     ESP_LOGCONFIG(TAG, "  Wait time %" PRId32 " ms", this->wait_time_); | ||||
| } | ||||
|  | ||||
| void HBridgeSwitch::write_state(bool state) { | ||||
|   this->desired_state_ = state; | ||||
|   if (!this->timer_running_) | ||||
|     this->timer_fn_(); | ||||
| } | ||||
|  | ||||
| void HBridgeSwitch::timer_fn_() { | ||||
|   uint32_t next_timeout = 0; | ||||
|  | ||||
|   while ((uint8_t) this->desired_state_ != this->relay_state_) { | ||||
|     switch (this->relay_state_) { | ||||
|       case RELAY_STATE_ON: | ||||
|       case RELAY_STATE_OFF: | ||||
|       case RELAY_STATE_UNKNOWN: | ||||
|         if (this->desired_state_) { | ||||
|           this->on_pin_->digital_write(true); | ||||
|           this->relay_state_ = RELAY_STATE_SWITCHING_ON; | ||||
|         } else { | ||||
|           this->off_pin_->digital_write(true); | ||||
|           this->relay_state_ = RELAY_STATE_SWITCHING_OFF; | ||||
|         } | ||||
|         next_timeout = this->pulse_length_; | ||||
|         if (!this->optimistic_) | ||||
|           this->publish_state(this->desired_state_); | ||||
|         break; | ||||
|  | ||||
|       case RELAY_STATE_SWITCHING_ON: | ||||
|         this->on_pin_->digital_write(false); | ||||
|         this->relay_state_ = RELAY_STATE_ON; | ||||
|         if (this->optimistic_) | ||||
|           this->publish_state(true); | ||||
|         next_timeout = this->wait_time_; | ||||
|         break; | ||||
|  | ||||
|       case RELAY_STATE_SWITCHING_OFF: | ||||
|         this->off_pin_->digital_write(false); | ||||
|         this->relay_state_ = RELAY_STATE_OFF; | ||||
|         if (this->optimistic_) | ||||
|           this->publish_state(false); | ||||
|         next_timeout = this->wait_time_; | ||||
|         break; | ||||
|     } | ||||
|  | ||||
|     if (next_timeout) { | ||||
|       this->timer_running_ = true; | ||||
|       this->set_timeout(next_timeout, [this]() { this->timer_fn_(); }); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     // In the case where ON/OFF state has been reached but we need to | ||||
|     // immediately change back again to reach desired_state_, we loop. | ||||
|   } | ||||
|   this->timer_running_ = false; | ||||
| } | ||||
|  | ||||
| }  // namespace hbridge | ||||
| }  // namespace esphome | ||||
							
								
								
									
										50
									
								
								esphome/components/hbridge/switch/hbridge_switch.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								esphome/components/hbridge/switch/hbridge_switch.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include "esphome/core/component.h" | ||||
| #include "esphome/core/hal.h" | ||||
| #include "esphome/components/switch/switch.h" | ||||
|  | ||||
| #include <vector> | ||||
|  | ||||
| namespace esphome { | ||||
| namespace hbridge { | ||||
|  | ||||
| enum RelayState : uint8_t { | ||||
|   RELAY_STATE_OFF = 0, | ||||
|   RELAY_STATE_ON = 1, | ||||
|   RELAY_STATE_SWITCHING_ON = 2, | ||||
|   RELAY_STATE_SWITCHING_OFF = 3, | ||||
|   RELAY_STATE_UNKNOWN = 4, | ||||
| }; | ||||
|  | ||||
| class HBridgeSwitch : public switch_::Switch, public Component { | ||||
|  public: | ||||
|   void set_on_pin(GPIOPin *pin) { this->on_pin_ = pin; } | ||||
|   void set_off_pin(GPIOPin *pin) { this->off_pin_ = pin; } | ||||
|   void set_pulse_length(uint32_t pulse_length) { this->pulse_length_ = pulse_length; } | ||||
|   void set_wait_time(uint32_t wait_time) { this->wait_time_ = wait_time; } | ||||
|   void set_optimistic(bool optimistic) { this->optimistic_ = optimistic; } | ||||
|  | ||||
|   // ========== INTERNAL METHODS ========== | ||||
|   // (In most use cases you won't need these) | ||||
|   float get_setup_priority() const override; | ||||
|  | ||||
|   void setup() override; | ||||
|   void dump_config() override; | ||||
|  | ||||
|  protected: | ||||
|   void write_state(bool state) override; | ||||
|   void timer_fn_(); | ||||
|  | ||||
|   bool timer_running_{false}; | ||||
|   bool desired_state_{false}; | ||||
|   RelayState relay_state_{RELAY_STATE_UNKNOWN}; | ||||
|   GPIOPin *on_pin_{nullptr}; | ||||
|   GPIOPin *off_pin_{nullptr}; | ||||
|   uint32_t pulse_length_{0}; | ||||
|   uint32_t wait_time_{0}; | ||||
|   bool optimistic_{false}; | ||||
| }; | ||||
|  | ||||
| }  // namespace hbridge | ||||
| }  // namespace esphome | ||||
							
								
								
									
										39
									
								
								tests/components/hbridge/common.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								tests/components/hbridge/common.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| output: | ||||
|   - platform: ${pwm_platform} | ||||
|     pin: ${output1_pin} | ||||
|     id: gpio_output1 | ||||
|   - platform: ${pwm_platform} | ||||
|     pin: ${output2_pin} | ||||
|     id: gpio_output2 | ||||
|   - platform: ${pwm_platform} | ||||
|     pin: ${output3_pin} | ||||
|     id: gpio_output3 | ||||
|   - platform: ${pwm_platform} | ||||
|     pin: ${output4_pin} | ||||
|     id: gpio_output4 | ||||
|  | ||||
| light: | ||||
|   - platform: hbridge | ||||
|     name: Icicle Lights | ||||
|     pin_a: gpio_output3 | ||||
|     pin_b: gpio_output4 | ||||
|  | ||||
| fan: | ||||
|   - platform: hbridge | ||||
|     id: fan_hbridge | ||||
|     speed_count: 4 | ||||
|     name: H-bridge Fan with Presets | ||||
|     pin_a: gpio_output1 | ||||
|     pin_b: gpio_output2 | ||||
|     preset_modes: | ||||
|       - Preset 1 | ||||
|       - Preset 2 | ||||
|     on_preset_set: | ||||
|       then: | ||||
|         - logger.log: Preset mode was changed! | ||||
|  | ||||
| switch: | ||||
|   - platform: hbridge | ||||
|     id: switch_hbridge | ||||
|     on_pin: ${hbridge_on_pin} | ||||
|     off_pin: ${hbridge_off_pin} | ||||
| @@ -1,33 +1,17 @@ | ||||
| output: | ||||
|   - platform: ledc | ||||
|     pin: 14 | ||||
|     id: gpio_output1 | ||||
|   - platform: ledc | ||||
|     pin: 15 | ||||
|     id: gpio_output2 | ||||
|   - platform: ledc | ||||
|     pin: 12 | ||||
|     id: gpio_output3 | ||||
|   - platform: ledc | ||||
|     pin: 13 | ||||
|     id: gpio_output4 | ||||
| substitutions: | ||||
|   pwm_platform: ledc | ||||
|   output1_pin: "14" | ||||
|   output2_pin: "15" | ||||
|   output3_pin: "12" | ||||
|   output4_pin: "13" | ||||
|   hbridge_on_pin: "4" | ||||
|   hbridge_off_pin: "5" | ||||
|  | ||||
| light: | ||||
|   - platform: hbridge | ||||
|     name: Icicle Lights | ||||
|     pin_a: gpio_output3 | ||||
|     pin_b: gpio_output4 | ||||
| packages: | ||||
|   common: !include common.yaml | ||||
|  | ||||
| fan: | ||||
|   - platform: hbridge | ||||
|     id: fan_hbridge | ||||
|     speed_count: 4 | ||||
|     name: H-bridge Fan with Presets | ||||
|     pin_a: gpio_output1 | ||||
|     pin_b: gpio_output2 | ||||
|     preset_modes: | ||||
|       - Preset 1 | ||||
|       - Preset 2 | ||||
|     on_preset_set: | ||||
|       then: | ||||
|         - logger.log: Preset mode was changed! | ||||
| switch: | ||||
|   - id: !extend switch_hbridge | ||||
|     pulse_length: 60ms | ||||
|     wait_time: 10ms | ||||
|     optimistic: false | ||||
|   | ||||
| @@ -1,33 +1,16 @@ | ||||
| output: | ||||
|   - platform: ledc | ||||
|     pin: 4 | ||||
|     id: gpio_output1 | ||||
|   - platform: ledc | ||||
|     pin: 5 | ||||
|     id: gpio_output2 | ||||
|   - platform: ledc | ||||
|     pin: 6 | ||||
|     id: gpio_output3 | ||||
|   - platform: ledc | ||||
|     pin: 7 | ||||
|     id: gpio_output4 | ||||
| substitutions: | ||||
|   pwm_platform: "ledc" | ||||
|   output1_pin: "4" | ||||
|   output2_pin: "5" | ||||
|   output3_pin: "6" | ||||
|   output4_pin: "7" | ||||
|   hbridge_on_pin: "2" | ||||
|   hbridge_off_pin: "3" | ||||
|  | ||||
| light: | ||||
|   - platform: hbridge | ||||
|     name: Icicle Lights | ||||
|     pin_a: gpio_output3 | ||||
|     pin_b: gpio_output4 | ||||
| packages: | ||||
|   common: !include common.yaml | ||||
|  | ||||
| fan: | ||||
|   - platform: hbridge | ||||
|     id: fan_hbridge | ||||
|     speed_count: 4 | ||||
|     name: H-bridge Fan with Presets | ||||
|     pin_a: gpio_output1 | ||||
|     pin_b: gpio_output2 | ||||
|     preset_modes: | ||||
|       - Preset 1 | ||||
|       - Preset 2 | ||||
|     on_preset_set: | ||||
|       then: | ||||
|         - logger.log: Preset mode was changed! | ||||
| switch: | ||||
|   - id: !extend switch_hbridge | ||||
|     wait_time: 10ms | ||||
|     optimistic: true | ||||
|   | ||||
| @@ -1,33 +1,15 @@ | ||||
| output: | ||||
|   - platform: ledc | ||||
|     pin: 4 | ||||
|     id: gpio_output1 | ||||
|   - platform: ledc | ||||
|     pin: 5 | ||||
|     id: gpio_output2 | ||||
|   - platform: ledc | ||||
|     pin: 6 | ||||
|     id: gpio_output3 | ||||
|   - platform: ledc | ||||
|     pin: 7 | ||||
|     id: gpio_output4 | ||||
| substitutions: | ||||
|   pwm_platform: "ledc" | ||||
|   output1_pin: "4" | ||||
|   output2_pin: "5" | ||||
|   output3_pin: "6" | ||||
|   output4_pin: "7" | ||||
|   hbridge_on_pin: "2" | ||||
|   hbridge_off_pin: "3" | ||||
|  | ||||
| light: | ||||
|   - platform: hbridge | ||||
|     name: Icicle Lights | ||||
|     pin_a: gpio_output3 | ||||
|     pin_b: gpio_output4 | ||||
| packages: | ||||
|   common: !include common.yaml | ||||
|  | ||||
| fan: | ||||
|   - platform: hbridge | ||||
|     id: fan_hbridge | ||||
|     speed_count: 4 | ||||
|     name: H-bridge Fan with Presets | ||||
|     pin_a: gpio_output1 | ||||
|     pin_b: gpio_output2 | ||||
|     preset_modes: | ||||
|       - Preset 1 | ||||
|       - Preset 2 | ||||
|     on_preset_set: | ||||
|       then: | ||||
|         - logger.log: Preset mode was changed! | ||||
| switch: | ||||
|   - id: !extend switch_hbridge | ||||
|     pulse_length: 60ms | ||||
|   | ||||
| @@ -1,33 +1,16 @@ | ||||
| output: | ||||
|   - platform: ledc | ||||
|     pin: 14 | ||||
|     id: gpio_output1 | ||||
|   - platform: ledc | ||||
|     pin: 15 | ||||
|     id: gpio_output2 | ||||
|   - platform: ledc | ||||
|     pin: 12 | ||||
|     id: gpio_output3 | ||||
|   - platform: ledc | ||||
|     pin: 13 | ||||
|     id: gpio_output4 | ||||
| substitutions: | ||||
|   pwm_platform: "ledc" | ||||
|   output1_pin: "14" | ||||
|   output2_pin: "15" | ||||
|   output3_pin: "12" | ||||
|   output4_pin: "13" | ||||
|   hbridge_on_pin: "4" | ||||
|   hbridge_off_pin: "5" | ||||
|  | ||||
| light: | ||||
|   - platform: hbridge | ||||
|     name: Icicle Lights | ||||
|     pin_a: gpio_output3 | ||||
|     pin_b: gpio_output4 | ||||
| packages: | ||||
|   common: !include common.yaml | ||||
|  | ||||
| fan: | ||||
|   - platform: hbridge | ||||
|     id: fan_hbridge | ||||
|     speed_count: 4 | ||||
|     name: H-bridge Fan with Presets | ||||
|     pin_a: gpio_output1 | ||||
|     pin_b: gpio_output2 | ||||
|     preset_modes: | ||||
|       - Preset 1 | ||||
|       - Preset 2 | ||||
|     on_preset_set: | ||||
|       then: | ||||
|         - logger.log: Preset mode was changed! | ||||
| switch: | ||||
|   - id: !extend switch_hbridge | ||||
|     pulse_length: 60ms | ||||
|     wait_time: 10ms | ||||
|   | ||||
| @@ -1,33 +1,16 @@ | ||||
| output: | ||||
|   - platform: esp8266_pwm | ||||
|     pin: 4 | ||||
|     id: gpio_output1 | ||||
|   - platform: esp8266_pwm | ||||
|     pin: 5 | ||||
|     id: gpio_output2 | ||||
|   - platform: esp8266_pwm | ||||
|     pin: 12 | ||||
|     id: gpio_output3 | ||||
|   - platform: esp8266_pwm | ||||
|     pin: 13 | ||||
|     id: gpio_output4 | ||||
| substitutions: | ||||
|   pwm_platform: "esp8266_pwm" | ||||
|   output1_pin: "4" | ||||
|   output2_pin: "5" | ||||
|   output3_pin: "12" | ||||
|   output4_pin: "13" | ||||
|   hbridge_on_pin: "14" | ||||
|   hbridge_off_pin: "15" | ||||
|  | ||||
| light: | ||||
|   - platform: hbridge | ||||
|     name: Icicle Lights | ||||
|     pin_a: gpio_output3 | ||||
|     pin_b: gpio_output4 | ||||
| packages: | ||||
|   common: !include common.yaml | ||||
|  | ||||
| fan: | ||||
|   - platform: hbridge | ||||
|     id: fan_hbridge | ||||
|     speed_count: 4 | ||||
|     name: H-bridge Fan with Presets | ||||
|     pin_a: gpio_output1 | ||||
|     pin_b: gpio_output2 | ||||
|     preset_modes: | ||||
|       - Preset 1 | ||||
|       - Preset 2 | ||||
|     on_preset_set: | ||||
|       then: | ||||
|         - logger.log: Preset mode was changed! | ||||
| switch: | ||||
|   - id: !extend switch_hbridge | ||||
|     pulse_length: 60ms | ||||
|     wait_time: 10ms | ||||
|   | ||||
| @@ -1,33 +1,16 @@ | ||||
| output: | ||||
|   - platform: rp2040_pwm | ||||
|     pin: 4 | ||||
|     id: gpio_output1 | ||||
|   - platform: rp2040_pwm | ||||
|     pin: 5 | ||||
|     id: gpio_output2 | ||||
|   - platform: rp2040_pwm | ||||
|     pin: 6 | ||||
|     id: gpio_output3 | ||||
|   - platform: rp2040_pwm | ||||
|     pin: 7 | ||||
|     id: gpio_output4 | ||||
| substitutions: | ||||
|   pwm_platform: "rp2040_pwm" | ||||
|   output1_pin: "4" | ||||
|   output2_pin: "5" | ||||
|   output3_pin: "6" | ||||
|   output4_pin: "7" | ||||
|   hbridge_on_pin: "2" | ||||
|   hbridge_off_pin: "3" | ||||
|  | ||||
| light: | ||||
|   - platform: hbridge | ||||
|     name: Icicle Lights | ||||
|     pin_a: gpio_output3 | ||||
|     pin_b: gpio_output4 | ||||
| packages: | ||||
|   common: !include common.yaml | ||||
|  | ||||
| fan: | ||||
|   - platform: hbridge | ||||
|     id: fan_hbridge | ||||
|     speed_count: 4 | ||||
|     name: H-bridge Fan with Presets | ||||
|     pin_a: gpio_output1 | ||||
|     pin_b: gpio_output2 | ||||
|     preset_modes: | ||||
|       - Preset 1 | ||||
|       - Preset 2 | ||||
|     on_preset_set: | ||||
|       then: | ||||
|         - logger.log: Preset mode was changed! | ||||
| switch: | ||||
|   - id: !extend switch_hbridge | ||||
|     wait_time: 10ms | ||||
|     optimistic: true | ||||
|   | ||||
		Reference in New Issue
	
	Block a user