Skip to content

Commit 1c97f9b

Browse files
firmware: make "Swap Buttons" a select (match Motor Direction UI)
Per the sibling install-orientation setting, convert the toggle from a template switch to a template select with named states ("Normal" / "Swapped ↔"), sitting right under Motor Direction in Setup. Like Motor Direction it holds no state of its own: it reads/writes a persisted global (global_btn_swap) and never fights the restored value. Behaviour is identical to the switch version. Verified: esphome config valid, compiles clean, OTA-flashed to VAL3000 @ .58 -- device boots with 'Swap Buttons' select showing "Normal". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 205a4f4 commit 1c97f9b

1 file changed

Lines changed: 40 additions & 25 deletions

File tree

‎firmware/common/ropener-product.yaml‎

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ globals:
8282
# whichever button is currently the close button ever increments its counter.
8383
- { id: b2_tap_count, type: int, restore_value: no, initial_value: '0' }
8484
- { id: b2_last_tap_ms, type: uint32_t, restore_value: no, initial_value: '0' }
85+
# Persisted button-swap state. Like global_stepper_direction, it is the single
86+
# source of truth behind a stateless "Swap Buttons" select (see the select block).
87+
- { id: global_btn_swap, type: bool, restore_value: yes, initial_value: 'false' }
8588

8689
# --- Stall handling (homing) ------------------------------------------------
8790
# The Ropener homes sensorlessly: drive at the closed end until StallGuard fires,
@@ -231,7 +234,7 @@ script:
231234

232235
# Hold-to-run engage (armed on press, fired after hold_run_ms).
233236
# Hold-to-run for each button. The MOTION depends on the button's current role
234-
# (set by the "Swap Buttons" switch): the close button drives close, the open
237+
# (set by the "Swap Buttons" select): the close button drives close, the open
235238
# button drives open. Button 1 is the close button when swap is OFF; Button 2 is
236239
# the close button when swap is ON.
237240
- id: b1_hold_check
@@ -245,7 +248,7 @@ script:
245248
- globals.set: { id: b1_hold_active, value: "true" }
246249
- globals.set: { id: b1_consumed, value: "true" }
247250
- if:
248-
condition: { lambda: 'return !id(btn_swap).state;' }
251+
condition: { lambda: 'return !id(global_btn_swap);' }
249252
then:
250253
- cover.close: ropener_cover
251254
- logger.log: "Button 1 held: closing while held"
@@ -263,7 +266,7 @@ script:
263266
- globals.set: { id: b2_hold_active, value: "true" }
264267
- globals.set: { id: b2_consumed, value: "true" }
265268
- if:
266-
condition: { lambda: 'return id(btn_swap).state;' }
269+
condition: { lambda: 'return id(global_btn_swap);' }
267270
then:
268271
- cover.close: ropener_cover
269272
- logger.log: "Button 2 held: closing while held"
@@ -369,7 +372,7 @@ binary_sensor:
369372
else:
370373
# Idle, single button. Behaviour depends on Button 1's role.
371374
- if:
372-
condition: { lambda: 'return !id(btn_swap).state;' }
375+
condition: { lambda: 'return !id(global_btn_swap);' }
373376
then:
374377
# CLOSE button: count taps (7 -> homing), defer the close.
375378
- script.stop: b1_tap_commit
@@ -414,7 +417,7 @@ binary_sensor:
414417
# Tap: the close button defers (allows 7-tap chaining); the open
415418
# button opens immediately.
416419
- if:
417-
condition: { lambda: 'return !id(btn_swap).state;' }
420+
condition: { lambda: 'return !id(global_btn_swap);' }
418421
then:
419422
- script.execute: b1_tap_commit
420423
else:
@@ -461,7 +464,7 @@ binary_sensor:
461464
else:
462465
# Idle, single button. Behaviour depends on Button 2's role.
463466
- if:
464-
condition: { lambda: 'return id(btn_swap).state;' }
467+
condition: { lambda: 'return id(global_btn_swap);' }
465468
then:
466469
# CLOSE button (swap ON): count taps (7 -> homing), defer close.
467470
- script.stop: b2_tap_commit
@@ -506,7 +509,7 @@ binary_sensor:
506509
# Tap: the close button defers (allows 7-tap chaining); the open
507510
# button opens immediately.
508511
- if:
509-
condition: { lambda: 'return id(btn_swap).state;' }
512+
condition: { lambda: 'return id(global_btn_swap);' }
510513
then:
511514
- script.execute: b2_tap_commit
512515
else:
@@ -581,6 +584,36 @@ select:
581584
global_speed->update();
582585
global_preferences->sync();
583586
587+
# Swap the two physical buttons' open/close roles -- same select UI as Motor
588+
# Direction, and its sibling install-orientation setting (sits right under it in
589+
# Setup). "Normal" = Button 1 closes, Button 2 opens (the shipped layout).
590+
# "Swapped" = Button 1 opens, Button 2 closes -- for a mirror-image install (e.g.
591+
# pull-rope on the left) where the flipped unit makes the buttons feel reversed.
592+
# The 7-tap homing gesture and the deferred single-tap always follow the CLOSE
593+
# button. Like Motor Direction it holds NO state of its own: it reads and writes
594+
# the persisted global_btn_swap, so it never fights the restored value. Only
595+
# remaps the on-device buttons; the web UI, Home Assistant, and the schedule
596+
# open/close are unaffected. (A left-side install usually wants Motor Direction =
597+
# Reversed too.)
598+
- platform: template
599+
name: "Swap Buttons"
600+
id: sel_btn_swap
601+
icon: "mdi:swap-horizontal"
602+
entity_category: CONFIG
603+
web_server: { sorting_weight: 2, sorting_group_id: group_setup }
604+
update_interval: 1s
605+
options:
606+
- "Normal"
607+
- "Swapped ↔"
608+
lambda: |-
609+
if (id(global_btn_swap)) return std::string("Swapped ↔");
610+
return std::string("Normal");
611+
set_action:
612+
- lambda: |-
613+
id(global_btn_swap) = (x.find("Swap") != std::string::npos);
614+
global_btn_swap->update();
615+
global_preferences->sync();
616+
584617
# --- Rename core tuning entities to Ropener's existing entity_ids -------------
585618
number:
586619
# Raw IRUN (register 1-31) is meaningless to users -- the friendly "Motor
@@ -743,24 +776,6 @@ switch:
743776
optimistic: true
744777
restore_mode: RESTORE_DEFAULT_OFF
745778

746-
# Swap the two physical buttons' open/close roles. Default OFF = Button 1 closes,
747-
# Button 2 opens (the shipped layout). ON = Button 1 opens, Button 2 closes -- for
748-
# a mirror-image install (e.g. the pull-rope on the left instead of the right)
749-
# where the unit sits flipped and the buttons otherwise feel reversed. The 7-tap
750-
# homing gesture and the deferred single-tap always follow the CLOSE button, so
751-
# homing lives on whichever button closes. This only remaps the on-device buttons;
752-
# the web UI, Home Assistant, and the schedule open/close are unaffected. (Motor
753-
# Direction, a separate setting, reverses the motor itself -- a left-side install
754-
# usually wants both.)
755-
- platform: template
756-
name: "Swap Buttons"
757-
id: btn_swap
758-
icon: "mdi:swap-horizontal"
759-
entity_category: CONFIG
760-
web_server: { sorting_weight: 3, sorting_group_id: group_setup }
761-
optimistic: true
762-
restore_mode: RESTORE_DEFAULT_OFF
763-
764779
text_sensor:
765780
# Surfaces StallGuard's arming state, which is otherwise invisible. The
766781
# failure it exists to catch: if TCOOLTHRS is below the cruise TSTEP for the

0 commit comments

Comments
 (0)