Hello fellow HA freaks =:-)
Here after ending at a long road of home automatizations for my H-Blinds,
my solution off a collection of diff solutions from other users (just borrowed)
And finally my end solution for my home (7 H-Blinds).
As Hardware I use a
- 12v power adapter
- 12v power connector
- 12v to 5v L7805CV convertor
- 5v stepper 28BYJ-48
- ESP32-C3 Super Mini (or XIAO-ESP32-C3 from seeed studio for better wifi)
- A4988 stepper controller
- Hall 3144 sensor
- No print, direct wiring connection
- My almost self 3d creation of the unit to make and build in your H-Blind
3D Print
My final (almost complete yaml file for ESPHome) :
I still need a solution / help for that global variables how to put that value into the stepper configuration (max_speed for example)
# 1) Press button for > 1 second to enter setup mode
# 2) Press button again to start the blind closing
# 3) Press button again when closed and blind starts to open (actually resets the stepper position to 0)
# 4) Press button again when blind is fully open
# 5) Job Done
# Button is also used to open/close the blind (must be fully open/closed first)
substitutions:
slug: luxaflex-2
area: Hobbykamer
friendly_name: Luxeflex 2
statusled_pin: 8
description: >
ESP$new_ip_adress :
$friendly_name
encryption_key: !secret esphome_luxeflex_2_encryption_key
new_ip_adress: !secret esphome_luxeflex_2_IP
esphome_ssid: !secret esphome_orb_ssid
esphome_pass: !secret esphome_orb_pass
esphome_network: !secret esphome_orb_network
<<: !include onboot/luxeflex.yaml
packages:
<<: !include_dir_named common
esp32:
board: esp32-c3-devkitm-1
framework:
type: esp-idf
output:
- platform: gpio
id: setupLED
pin: GPIO05
inverted: False
stepper:
- platform: a4988
id: my_stepper
dir_pin:
number: GPIO01 # GPIO06 for seeed studio XIAO-ESP32-C3
inverted: True
step_pin: GPIO03 # GPIO07 for seeed studio XIAO-ESP32-C3
sleep_pin: GPIO00 # GPIO21 for seeed studio XIAO-ESP32-C3
max_speed: 500 steps/s # Set the speed of the motor
globals:
- id: stepper_slow_speed
type: float
initial_value: '300'
- id: stepper_normal_speed
type: float
initial_value: '500'
- id: my_stepper_global # Integer for storing the stepper position in case of reboot
type: int
restore_value: True
initial_value: '0'
- id: openclosed # Boolean to store OPEN/CLOSED state
type: bool
restore_value: True
initial_value: '0'
- id: endstop # Variable for storing ENDSTOP (how far to move stepper)
type: int
restore_value: True
initial_value: '10000'
- id: settingmode # Integer for Setup Mode
type: int
restore_value: False
initial_value: '0'
binary_sensor:
- platform: gpio
pin:
number: GPIO04 # HALL Sensor # GPIO20 for seeed studio XIAO-ESP32-C3
mode: INPUT_PULLUP
inverted: True
use_interrupt: True
interrupt_type: ANY
name: HALL_sensor
id: hall_sensor
internal: False
on_press:
then:
- logger.log:
format: "Hall Switch Triggered"
level: ERROR
- lambda: 'id(blinded).publish_state("0");'
- stepper.report_position:
id: my_stepper
position: 0
- if:
condition:
- lambda: 'return id(settingmode) == 2;'
then:
- script.execute: setupbutton
switch:
- platform: template
name: Roller Blind Setup Switch # Switch to enter Setup Mode
id: setupswitch
lambda: |-
if (id(settingmode) != 0) {
return true;
} else {
return false;
}
turn_on_action:
then:
- logger.log: "Entered Settings Mode"
- globals.set:
id: settingmode
value: '1'
turn_off_action:
then:
- logger.log: "Exiting Settings Mode"
- globals.set:
id: settingmode
value: '0'
- platform: template
name: Roller Blind Setup Button # Switch to replicate the Physical Button
id: hasetup
turn_on_action:
- if: # If settings variable is on
condition:
- lambda: 'return id(settingmode) != 0;'
then: # Enter Setting Mode
- output.turn_on : setupLED
- script.execute: setupbutton
- switch.turn_off: hasetup
cover:
- platform: template
name: ${slug}
id: blinded
open_action:
then:
- logger.log: "Opening"
- stepper.set_target: # Send stepper to endstop
id: my_stepper
target: !lambda return id(endstop);
- while:
condition:
lambda: 'return id(my_stepper).current_position != id(endstop);'
then:
- cover.template.publish:
id: blinded
position: !lambda 'return (float(float(id(my_stepper).current_position) / float(id(endstop))));'
current_operation: OPENING
- delay: 1000 ms
- globals.set: # Set global to current position
id: my_stepper_global
value: !lambda return id(my_stepper).current_position;
- globals.set: # Set toggle to OPEN (No need for 'optimistic mode')
id: openclosed
value: '1'
- cover.template.publish:
id: blinded
state: OPEN
current_operation: IDLE
close_action:
then:
- logger.log: "Closing"
- stepper.set_target: # Send stepper to 0
id: my_stepper
target: '0'
- while:
condition:
lambda: 'return id(my_stepper).current_position != 0;'
then:
- cover.template.publish:
id: blinded
position: !lambda 'return (float(float(id(my_stepper).current_position) / float(id(endstop))));'
current_operation: CLOSING
- delay: 1000 ms
- globals.set: # Set global to current position
id: my_stepper_global
value: !lambda return id(my_stepper).current_position;
- globals.set: # Set toggle to CLOSED (No need for 'optimistic mode')
id: openclosed
value: '0'
- cover.template.publish:
id: blinded
state: CLOSED
current_operation: IDLE
position_action:
then:
- stepper.set_target:
id: my_stepper
target: !lambda return int(id(endstop) * pos);
- while:
condition:
lambda: 'return id(my_stepper).current_position != int(id(endstop) * pos);'
then:
- cover.template.publish:
id: blinded
position: !lambda 'return (float(float(id(my_stepper).current_position) / float(id(endstop))));'
- delay: 1000 ms
- globals.set: # Set global to current position
id: my_stepper_global
value: !lambda return id(my_stepper).current_position;
- cover.template.publish:
id: blinded
position: !lambda 'return (float(float(id(my_stepper).current_position) / float(id(endstop))));'
current_operation: IDLE
stop_action:
then:
- stepper.set_target:
id: my_stepper
target: !lambda return id(my_stepper).current_position;
- globals.set: # Set global to current position
id: my_stepper_global
value: !lambda return id(my_stepper).current_position;
- cover.template.publish:
id: blinded
position: !lambda 'return (float(float(id(my_stepper).current_position) / float(id(endstop))));'
current_operation: IDLE
has_position: true
device_class: blind
script:
- id: setupbutton
then:
- if:
condition:
- lambda: 'return (id(settingmode) == 3);'
then:
- output.turn_off : setupLED
- logger.log: "-= Setup Button: Mode 3 =- (end setup)"
- stepper.set_target: # Set Stepper position
id: my_stepper
target: !lambda return id(my_stepper).current_position;
- globals.set: # Set Endstop Variable
id: endstop
value: !lambda return id(my_stepper).current_position;
- globals.set: # Set Global stepper position
id: my_stepper_global
value: !lambda return id(my_stepper).current_position;
- globals.set: # Reset Setting Mode
id: settingmode
value: '0'
- globals.set: # Set toggle to Open
id: openclosed
value: '1'
- cover.template.publish:
id: blinded
state: OPEN
current_operation: IDLE
- logger.log: "Exiting Setting Mode"
- stepper.set_speed:
id: my_stepper
speed: !lambda 'return id(stepper_normal_speed);'
- if:
condition:
- lambda: 'return (id(settingmode) == 2);'
then:
- logger.log: "-= Setup Button: Mode 2 =-"
- stepper.set_speed:
id: my_stepper
speed: !lambda 'return id(stepper_slow_speed);'
- stepper.report_position: # Reset Stepper position to 0
id: my_stepper
position: '0'
- stepper.set_target: # Reset Stepper position to 0
id: my_stepper
target: '0'
- globals.set: # Move stepper to 0 (doesn't move it's already there!)
id: my_stepper_global
value: '0'
- delay: 100ms
- stepper.set_target: # Reset Stepper position to 72000
id: my_stepper
target: '72000'
- globals.set: # Advance setup to next mode
id: settingmode
value: '3'
- if:
condition:
- lambda: 'return (id(settingmode) == 1);'
then:
- logger.log: "-= Setup mode: Mode 1 =-"
- stepper.report_position: # Set Stepper position to 72000, makes it move to 0 (Closed)
id: my_stepper
position: '72000'
- globals.set: # Advance setup to next mode
id: settingmode
value: '2'
button:
- platform: template
name: "Reset to '0'"
internal: False
on_press:
- if:
condition:
binary_sensor.is_off: hall_sensor
then:
- stepper.report_position: # Set Stepper position to 72000, makes it move to 0 (Closed)
id: my_stepper
position: '72000'
- globals.set: # Set toggle to CLOSED (No need for 'optimistic mode')
id: openclosed
value: '0'
- cover.template.publish:
id: blinded
state: CLOSED
current_operation: IDLE
If you where looking for such solution too (like me for a while and finally made my almost own gathered and self creation) :
Good luck and have FUN building it !!
If I forget something … just ask me.
GreetingZzz John,
-= May the automatizations be with you ! =-
PS I forget I have a non standard HA structure and I hope that this code works on a standard HA structure what I cannot test here ![]()
# 1) Press button for > 1 second to enter setup mode
# 2) Press button again to start the blind closing
# 3) Press button again when closed and blind starts to open (actually resets the stepper position to 0)
# 4) Press button again when blind is fully open
# 5) Job Done
# Button is also used to open/close the blind (must be fully open/closed first)
substitutions:
slug: luxaflex-2
area: Hobbykamer
friendly_name: Luxeflex 2
statusled_pin: 8 # depend on board-type
description: >
ESP$new_ip_adress :
$friendly_name
encryption_key: !secret esphome_luxeflex_2_encryption_key
new_ip_adress: !secret esphome_luxeflex_2_IP
esphome_ssid: !secret esphome_orb_ssid
esphome_pass: !secret esphome_orb_pass
esphome_network: !secret esphome_orb_network
esphome:
name: "${slug}"
comment: "${description}"
build_path: "./.build/${slug}/"
# arduino_version: latest
on_boot:
- priority: -200.0
then:
- stepper.report_position: # Set stepper to global variable
id: my_stepper
position: !lambda return id(my_stepper_global);
- stepper.set_target: # Set stepper to global variable
id: my_stepper
target: !lambda return id(my_stepper_global);
- if: # If blind is Closed
condition:
- lambda: 'return id(my_stepper_global) == 0;'
then: # Publish state etc.
- cover.template.publish:
id: blinded
state: CLOSED
current_operation: IDLE
- if: # If blind is Open
condition:
- lambda: 'return id(my_stepper_global) == id(endstop);'
then: # Publish state etc.
- cover.template.publish:
id: blinded
state: OPEN
current_operation: IDLE
- if: # If blind is Neither
condition:
- lambda: 'return (id(my_stepper_global) != 0) && (id(my_stepper_global) != id(endstop));'
then: # # Publish state etc.
- cover.template.publish:
id: blinded
position: !lambda 'return (float(float(id(my_stepper).current_position) / float(id(endstop))));'
current_operation: IDLE
preferences:
flash_write_interval: 5min
esp32:
board: esp32-c3-devkitm-1
framework:
type: esp-idf
api:
encryption:
key: "V71q0lU1V+7wG7GInlBh1yBXWYid2XFAqiWSHcw5XcQ="
reboot_timeout: 5min
wifi:
networks:
- id: component_wifi
- ssid: $esphome_ssid
password: $esphome_pass
power_save_mode: none
fast_connect: true
min_auth_mode: WPA2
manual_ip:
# Set this to the IP of the ESP
static_ip: "0.0.0.0"
# Set this to the IP address of the router. Often ends with .1
gateway: "0.0.0.0"
# The subnet of the network. 255.255.255.0 works for most home networks.
subnet: 255.255.255.0
# use_address: "${slug}.local" # unmark this line if IP-adres was changed for some reason
reboot_timeout: 10min
ap:
ssid: $slug
password: !secret esphome_fbw_password
logger:
id: component_logger
level: VERY_VERBOSE
initial_level: DEBUG
# Disabling logging over UART by default.
# Some devices this is needed, and in general,
# I really don't look at it.
baud_rate: 0
select:
- platform: logger
name: "Logger select"
ota:
- platform: esphome
id: my_ota
password: "${ota_password}"
safe_mode:
reboot_timeout: 10min
num_attempts: 5
# unmark if changed the OTA password :
#esphome:
# on_boot:
# - lambda: |-
# id(my_ota).set_auth_password("");
web_server:
port: 80
auth:
username: !secret esphome_web_server_username
password: !secret esphome_web_server_password
captive_portal: ~
output:
- platform: gpio
id: setupLED
pin: GPIO05
inverted: False
light:
- platform: status_led
name: "Statusled"
id: statusled
icon: mdi:led-outline
internal: False
pin:
number: $statusled_pin
inverted: true
stepper:
- platform: a4988
id: my_stepper
dir_pin:
number: GPIO01 # GPIO06 for seeed studio XIAO-ESP32-C3
inverted: True
step_pin: GPIO03 # GPIO07 for seeed studio XIAO-ESP32-C3
sleep_pin: GPIO00 # GPIO21 for seeed studio XIAO-ESP32-C3
max_speed: 500 steps/s # Set the speed of the motor
globals:
- id: stepper_slow_speed
type: float
initial_value: '300'
- id: stepper_normal_speed
type: float
initial_value: '500'
- id: my_stepper_global # Integer for storing the stepper position in case of reboot
type: int
restore_value: True
initial_value: '0'
- id: openclosed # Boolean to store OPEN/CLOSED state
type: bool
restore_value: True
initial_value: '0'
- id: endstop # Variable for storing ENDSTOP (how far to move stepper)
type: int
restore_value: True
initial_value: '10000'
- id: settingmode # Integer for Setup Mode
type: int
restore_value: False
initial_value: '0'
sensor:
- platform: wifi_signal # Reports the WiFi signal strength in %
name: "$slug signal"
id: wifi_signal_percent
update_interval: 60s
filters:
- lambda: return min(max(2 * (x + 100.0), 0.0), 100.0);
- heartbeat: 30s
unit_of_measurement: " %"
entity_category: diagnostic
binary_sensor:
- platform: gpio
pin:
number: GPIO04 # HALL Sensor # GPIO20 for seeed studio XIAO-ESP32-C3
mode: INPUT_PULLUP
inverted: True
use_interrupt: True
interrupt_type: ANY
name: HALL_sensor
id: hall_sensor
internal: False
on_press:
then:
- logger.log:
format: "Hall Switch Triggered"
level: ERROR
- lambda: 'id(blinded).publish_state("0");'
- stepper.report_position:
id: my_stepper
position: 0
- if:
condition:
- lambda: 'return id(settingmode) == 2;'
then:
- script.execute: setupbutton
switch:
- platform: template
name: Roller Blind Setup Switch # Switch to enter Setup Mode
id: setupswitch
lambda: |-
if (id(settingmode) != 0) {
return true;
} else {
return false;
}
turn_on_action:
then:
- logger.log: "Entered Settings Mode"
- globals.set:
id: settingmode
value: '1'
turn_off_action:
then:
- logger.log: "Exiting Settings Mode"
- globals.set:
id: settingmode
value: '0'
- platform: template
name: Roller Blind Setup Button # Switch to replicate the Physical Button
id: hasetup
turn_on_action:
- if: # If settings variable is on
condition:
- lambda: 'return id(settingmode) != 0;'
then: # Enter Setting Mode
- output.turn_on : setupLED
- script.execute: setupbutton
- switch.turn_off: hasetup
cover:
- platform: template
name: ${slug}
id: blinded
open_action:
then:
- logger.log: "Opening"
- stepper.set_target: # Send stepper to endstop
id: my_stepper
target: !lambda return id(endstop);
- while:
condition:
lambda: 'return id(my_stepper).current_position != id(endstop);'
then:
- cover.template.publish:
id: blinded
position: !lambda 'return (float(float(id(my_stepper).current_position) / float(id(endstop))));'
current_operation: OPENING
- delay: 1000 ms
- globals.set: # Set global to current position
id: my_stepper_global
value: !lambda return id(my_stepper).current_position;
- globals.set: # Set toggle to OPEN (No need for 'optimistic mode')
id: openclosed
value: '1'
- cover.template.publish:
id: blinded
state: OPEN
current_operation: IDLE
close_action:
then:
- logger.log: "Closing"
- stepper.set_target: # Send stepper to 0
id: my_stepper
target: '0'
- while:
condition:
lambda: 'return id(my_stepper).current_position != 0;'
then:
- cover.template.publish:
id: blinded
position: !lambda 'return (float(float(id(my_stepper).current_position) / float(id(endstop))));'
current_operation: CLOSING
- delay: 1000 ms
- globals.set: # Set global to current position
id: my_stepper_global
value: !lambda return id(my_stepper).current_position;
- globals.set: # Set toggle to CLOSED (No need for 'optimistic mode')
id: openclosed
value: '0'
- cover.template.publish:
id: blinded
state: CLOSED
current_operation: IDLE
position_action:
then:
- stepper.set_target:
id: my_stepper
target: !lambda return int(id(endstop) * pos);
- while:
condition:
lambda: 'return id(my_stepper).current_position != int(id(endstop) * pos);'
then:
- cover.template.publish:
id: blinded
position: !lambda 'return (float(float(id(my_stepper).current_position) / float(id(endstop))));'
- delay: 1000 ms
- globals.set: # Set global to current position
id: my_stepper_global
value: !lambda return id(my_stepper).current_position;
- cover.template.publish:
id: blinded
position: !lambda 'return (float(float(id(my_stepper).current_position) / float(id(endstop))));'
current_operation: IDLE
stop_action:
then:
- stepper.set_target:
id: my_stepper
target: !lambda return id(my_stepper).current_position;
- globals.set: # Set global to current position
id: my_stepper_global
value: !lambda return id(my_stepper).current_position;
- cover.template.publish:
id: blinded
position: !lambda 'return (float(float(id(my_stepper).current_position) / float(id(endstop))));'
current_operation: IDLE
has_position: true
device_class: blind
script:
- id: setupbutton
then:
- if:
condition:
- lambda: 'return (id(settingmode) == 3);'
then:
- output.turn_off : setupLED
- logger.log: "-= Setup Button: Mode 3 =- (end setup)"
- stepper.set_target: # Set Stepper position
id: my_stepper
target: !lambda return id(my_stepper).current_position;
- globals.set: # Set Endstop Variable
id: endstop
value: !lambda return id(my_stepper).current_position;
- globals.set: # Set Global stepper position
id: my_stepper_global
value: !lambda return id(my_stepper).current_position;
- globals.set: # Reset Setting Mode
id: settingmode
value: '0'
- globals.set: # Set toggle to Open
id: openclosed
value: '1'
- cover.template.publish:
id: blinded
state: OPEN
current_operation: IDLE
- logger.log: "Exiting Setting Mode"
- stepper.set_speed:
id: my_stepper
speed: !lambda 'return id(stepper_normal_speed);'
- if:
condition:
- lambda: 'return (id(settingmode) == 2);'
then:
- logger.log: "-= Setup Button: Mode 2 =-"
- stepper.set_speed:
id: my_stepper
speed: !lambda 'return id(stepper_slow_speed);'
- stepper.report_position: # Reset Stepper position to 0
id: my_stepper
position: '0'
- stepper.set_target: # Reset Stepper position to 0
id: my_stepper
target: '0'
- globals.set: # Move stepper to 0 (doesn't move it's already there!)
id: my_stepper_global
value: '0'
- delay: 100ms
- stepper.set_target: # Reset Stepper position to 72000
id: my_stepper
target: '72000'
- globals.set: # Advance setup to next mode
id: settingmode
value: '3'
- if:
condition:
- lambda: 'return (id(settingmode) == 1);'
then:
- logger.log: "-= Setup mode: Mode 1 =-"
- stepper.report_position: # Set Stepper position to 72000, makes it move to 0 (Closed)
id: my_stepper
position: '72000'
- globals.set: # Advance setup to next mode
id: settingmode
value: '2'
button:
- platform: restart
id: button_restart
name: "Restart"
- platform: template
name: "Reset to '0'"
internal: False
on_press:
- if:
condition:
binary_sensor.is_off: hall_sensor
then:
- stepper.report_position: # Set Stepper position to 72000, makes it move to 0 (Closed)
id: my_stepper
position: '72000'
- globals.set: # Set toggle to CLOSED (No need for 'optimistic mode')
id: openclosed
value: '0'
- cover.template.publish:
id: blinded
state: CLOSED
text_sensor:
- platform: wifi_info
ip_address:
name: "IP Address"
icon: mdi:ip-network
ssid:
name: "SSID"
icon: mdi:wifi-settings
bssid:
name: "BSSID"
icon: mdi:identifier
- platform: version
id: text_sensor_version
name: "ESPHome Version"
hide_timestamp: true