From 1ba53e64da92960698feb72a8493d49de2caddee Mon Sep 17 00:00:00 2001 From: manuel Date: Wed, 8 Apr 2026 00:25:39 +0200 Subject: add office desk control --- esphome/config/office-desk-control.yaml | 290 ++++++++++++++++++++++++++++++++ 1 file changed, 290 insertions(+) create mode 100644 esphome/config/office-desk-control.yaml diff --git a/esphome/config/office-desk-control.yaml b/esphome/config/office-desk-control.yaml new file mode 100644 index 0000000..1d4dcba --- /dev/null +++ b/esphome/config/office-desk-control.yaml @@ -0,0 +1,290 @@ +substitutions: + device_name: office-desk-control + friendly_name: "Office Desk Control" + min_height: "65.5" # cm + max_height: "131" # cm + tx_pin: GPIO1 # TX + rx_pin: GPIO3 # RX + #tx_pin: GPIO15 # D7/TXD2 + #rx_pin: GPIO13 # D8/RXD2 + screen_pin: GPIO12 # D6 + +external_components: + source: github://iMicknl/LoctekMotion_IoT + components: [ loctekmotion_desk_height ] + +esphome: + name: ${device_name} + friendly_name: ${friendly_name} + + # Delay UART interaction on boot to give the desk controller time to initialize. + # After the delay, wake the desk by sending the "M" command to request the current height. + on_boot: + priority: -100 + then: + - delay: 10s + - button.press: button_wake_screen + - delay: 300ms + - button.press: button_m + +esp8266: + board: d1_mini + +# Enable logging +logger: + +# Enable Home Assistant API +api: + encryption: + key: !secret api_encryption_key + +# OTA +ota: + - platform: esphome + password: !secret ota_password + +# WiFi Credentials +wifi: + domain: .lan + ssid: !secret wifi_ssid + password: !secret wifi_password + power_save_mode: LIGHT + + # Enable fallback hotspot (captive portal) in case wifi connection fails + ap: + ssid: "${device_name} Hotspot" + password: !secret wifi_ap_password + +captive_portal: + +uart: + - id: desk_uart + baud_rate: 9600 + tx_pin: ${tx_pin} + rx_pin: ${rx_pin} + # debug: + +sensor: + - platform: uptime + name: Uptime + + - platform: loctekmotion_desk_height + id: "desk_height" + name: Desk height + on_value_range: + - below: ${min_height} + then: + - switch.turn_off: switch_down + - button.press: button_wake_screen + - above: ${max_height} + then: + - switch.turn_off: switch_up + - button.press: button_wake_screen + on_value: + then: + - cover.template.publish: + id: desk_cover + position: !lambda |- + // The sensor outputs values from min_height (cm) to max_height (cm) + // We need to translate this to 0 - 1 scale. + float position = (float(x) - float(${min_height})) / (float(${max_height}) - float(${min_height})); + return position; + - component.update: set_desk_height + +switch: + - platform: gpio + name: "Virtual screen" # PIN20 + pin: + number: ${screen_pin} + mode: OUTPUT + restore_mode: ALWAYS_ON + entity_category: "config" + internal: true + + - platform: uart + name: "Up" + id: switch_up + icon: mdi:arrow-up-bold + data: [0x9b, 0x06, 0x02, 0x01, 0x00, 0xfc, 0xa0, 0x9d] + uart_id: desk_uart + send_every: 108ms + internal: true + on_turn_on: + - button.press: button_wake_screen + - delay: 200ms + + - platform: uart + name: "Down" + id: switch_down + icon: mdi:arrow-down-bold + data: [0x9b, 0x06, 0x02, 0x02, 0x00, 0x0c, 0xa0, 0x9d] + uart_id: desk_uart + send_every: 108ms + internal: true + on_turn_on: + - button.press: button_wake_screen + - delay: 200ms + +# - platform: uart +# name: "Alarm off" +# id: switch_alarm +# icon: mdi:alarm +# data: [0x9b, 0x06, 0x02, 0x40, 0x00, 0xAC, 0x90, 0x9d] +# uart_id: desk_uart +# send_every: 108ms +# on_turn_on: +# - delay: 3000ms +# - switch.turn_off: switch_alarm +# entity_category: "config" + + - platform: uart + name: "Child lock" + id: switch_child_lock + icon: mdi:account-lock + data: [0x9b, 0x06, 0x02, 0x20, 0x00, 0xac, 0xb8, 0x9d] + uart_id: desk_uart + send_every: 108ms + on_turn_on: + - delay: 5000ms + - switch.turn_off: switch_child_lock + entity_category: "config" + +button: + - platform: template + name: "Preset 1" + icon: mdi:numeric-1-box + on_press: + - button.press: button_wake_screen + - delay: 200ms + - uart.write: + id: desk_uart + data: [0x9b, 0x06, 0x02, 0x04, 0x00, 0xac, 0xa3, 0x9d] + + - platform: template + name: "Preset 2" + icon: mdi:numeric-2-box + on_press: + - button.press: button_wake_screen + - delay: 200ms + - uart.write: + id: desk_uart + data: [0x9b, 0x06, 0x02, 0x08, 0x00, 0xac, 0xa6, 0x9d] + + - platform: template + name: "Sit" # Preset 3 on some control panels + icon: mdi:chair-rolling + on_press: + - button.press: button_wake_screen + - delay: 200ms + - uart.write: + id: desk_uart + data: [0x9b, 0x06, 0x02, 0x00, 0x01, 0xac, 0x60, 0x9d] + + - platform: template + name: "Stand" # Preset 4 on some control panels + icon: mdi:human-handsup + on_press: + - button.press: button_wake_screen + - delay: 200ms + - uart.write: + id: desk_uart + data: [0x9b, 0x06, 0x02, 0x10, 0x00, 0xac, 0xac, 0x9d] + + - platform: template + name: "Memory" + id: button_m + icon: mdi:alpha-m-box + entity_category: "config" + on_press: + - uart.write: + id: desk_uart + data: [0x9b, 0x06, 0x02, 0x20, 0x00, 0xac, 0xb8, 0x9d] + + - platform: template + name: "Wake screen" + id: button_wake_screen + icon: mdi:gesture-tap-button + entity_category: "config" + on_press: + - uart.write: + id: desk_uart + data: [0x9b, 0x06, 0x02, 0x00, 0x00, 0x6c, 0xa1, 0x9d] + +# - platform: template +# name: "Alarm" +# id: button_alarm +# icon: mdi:alarm +# on_press: +# - uart.write: +# id: desk_uart +# data: [0x9b, 0x06, 0x02, 0x40, 0x00, 0xAC, 0x90, 0x9d] + + - platform: restart + name: "Restart" + entity_category: "config" + +cover: + - platform: template + id: "desk_cover" + icon: mdi:desk # or mdi:human-male-height-variant + name: "Desk" + device_class: blind # makes it easier to integrate with Google/Alexa + has_position: true + position_action: + - if: + condition: + - lambda: !lambda |- + return pos > id(desk_cover).position; + then: + - cover.open: desk_cover + - wait_until: + lambda: |- + return id(desk_cover).position >= pos; + - cover.stop: desk_cover + else: + - cover.close: desk_cover + - wait_until: + lambda: |- + return id(desk_cover).position <= pos; + - cover.stop: desk_cover + stop_action: + - switch.turn_off: switch_up + - switch.turn_off: switch_down + - button.press: button_wake_screen + open_action: + - switch.turn_off: switch_down + - switch.turn_on: switch_up + close_action: + - switch.turn_off: switch_up + - switch.turn_on: switch_down + optimistic: false + +number: + - platform: template + name: "Desk height" + id: set_desk_height + min_value: ${min_height} + max_value: ${max_height} + icon: "mdi:counter" + unit_of_measurement: "cm" + device_class: "distance" + step: 0.1 + lambda: !lambda |- + return id(desk_height).state; + set_action: + - if: + condition: + - lambda: !lambda |- + return x > id(desk_height).state; + then: + - cover.open: desk_cover + - wait_until: + lambda: |- + return id(desk_height).state >= x; + - cover.stop: desk_cover + else: + - cover.close: desk_cover + - wait_until: + lambda: |- + return id(desk_height).state <= x; + - cover.stop: desk_cover -- cgit v1.2.3