Desky Standing Desk (ESPHome) [Works with Desky, Uplift, Jiecang, Assmann & others]

This is for a Vari Desk, and works great, thank you for getting me going!

I’ve made a few changes and additions to the code, including making functional minimum and maximum values to stop movement, and added presets for sitting and standing (seemed easier than trying to simulate the physical buttons).

I also had to change the throttle on the height from 200ms to 4ms to overcome an issue whereby the height displayed in HA did not match what was displayed on the desk. It also appears to have made it a bit more accurate now as a full run from standing to sitting stopped EXACTLY where it should have, whereby it was previously off .1-.2".

I originally tried going from 200ms to 100ms and it was better, but still off once in a while. I chose 4ms because of this article: https://www.devmire.com/reverse-engineering-a-standing-desk-for-fun-and-profit/ Specifically, there was a comment in the code that stated “Height should be set in bursts of x ms pulses, as it takes 4ms to read the current height”.

I used an ESP32, Dupont jumpers (male/female), and one of these for pass-thru: Amazon.com: Teansic 2PCS RJ45 Ethernet Dual Female Terminal Breakout Board,3.5mm Pitch 8Pin RJ45 Screw Connector Board Shielded Network Adapter Terminal : Electronics All that’s needed for assembly is a tiny little screwdriver - no soldering!

I’ll be designing an enclosure for it in the near future.

Enjoy!

esphome:
  name: small-desk
  friendly_name: Small Desk
  on_boot:
    priority: -100.0
    then:
    #Request a desk height update after boot.
      - delay: 5s
      - switch.turn_on: wake_desk_and_get_height

esp32:
  board: esp32dev
  framework:
    type: arduino

# Enable logging
logger:

# Enable Home Assistant API
api:

ota:

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:


external_components:
#Fetch ssieb's custom component# https://github.com/ssieb/custom_components/tree/master/components/desky
  - source:
      type: git
      url: https://github.com/ssieb/custom_components
    components: [ desky ]

uart:
  - id: desk_uart
    baud_rate: 9600
    rx_pin: 1

desky:
  id: desk_controller
  timeout: 15s
  height:
    name: Desk Height
    id: desk_height
    accuracy_decimals: 1
    unit_of_measurement: in
    filters:
    - delta: 0.05
    - throttle: 4ms
    - multiply: 0.1
    on_value:
      then:
          #If the value changes, then the desk is moving
        - binary_sensor.template.publish:
            id: desk_is_moving
            state: ON
        - delay: 300ms
          #Assume it's stopped moving if no height changes after a short time.
        - binary_sensor.template.publish:
            id: desk_is_moving
            state: Off
binary_sensor:
  - platform: template
    id: desk_is_moving
    name: "Desk Is Moving"
    filters:
      - delayed_off: 400ms
    #If the desk isn't moving for a bit we better turn off attempts at movement. Poor man's collision detection? 
    on_release:
      then:
        - button.press: desky_stop_desk

button: 
#Stop movement 
  - platform: template
    name: Stop Desk
    id: desky_stop_desk
    on_press:
      then:
        - switch.turn_off: raise_desk
        - switch.turn_off: lower_desk

#Go to height x
  - platform: template
    name: Go To Height x
    id: go_to_preset_height_x
    on_press:
      then:
      #Check if we need to move desk up or down from current position      
        if:
          condition:
          #Current height is more than target height, then move desk down
            lambda: |-
              return id(desk_target_height).state < id(desk_height).state;
          then:
            - switch.turn_on: lower_desk
            - wait_until:
              #Run until the difference between current and target state is < stopping distance 
                condition:
                  lambda: return (id(desk_height).state)<((id(desk_target_height).state) + (id(stopping_distance_in).state));
            - switch.turn_off: lower_desk
          else:
          #Current height is less than target height, move desk up
            - switch.turn_on: raise_desk
            - wait_until:
                condition:
                  lambda: return (id(desk_height).state)>((id(desk_target_height).state) - (id(stopping_distance_in).state));
                  #Run until the difference between current and target state is <0.3cm
            - switch.turn_off: raise_desk

#Go to sitting height
  - platform: template
    name: Go To Sitting Height
    id: go_to_sitting_preset_height
    on_press:
      then:
      #Check if we need to move desk up or down from current position      
        if:
          condition:
          #Current height is more than sitting height, then move desk down
            lambda: |-
              return id(desk_sitting_height).state < id(desk_height).state;
          then:
            - switch.turn_on: lower_desk
            - wait_until:
              #Run until the difference between current and sitting height is < stopping distance 
                condition:
                  lambda: return (id(desk_height).state)<((id(desk_sitting_height).state) + (id(stopping_distance_in).state));
            - switch.turn_off: lower_desk
          else:
          #Current height is less than target height, move desk up
            - switch.turn_on: raise_desk
            - wait_until:
                condition:
                  lambda: return (id(desk_height).state)>((id(desk_sitting_height).state) - (id(stopping_distance_in).state));
                  #Run until the difference between current and sitting height < stopping distance
            - switch.turn_off: raise_desk


#Go to standing height
  - platform: template
    name: Go To Standing Height
    id: go_to_standing_preset_height
    on_press:
      then:
      #Check if we need to move desk up or down from current position      
        if:
          condition:
          #Current height is more than standing height, then move desk down
            lambda: |-
              return id(desk_standing_height).state < id(desk_height).state;
          then:
            - switch.turn_on: lower_desk
            - wait_until:
              #Run until the difference between current and standing height is < stopping distance 
                condition:
                  lambda: return (id(desk_height).state)<((id(desk_standing_height).state) + (id(stopping_distance_in).state));
            - switch.turn_off: lower_desk
          else:
          #Current height is less than target height, move desk up
            - switch.turn_on: raise_desk
            - wait_until:
                condition:
                  lambda: return (id(desk_height).state)>((id(desk_standing_height).state) - (id(stopping_distance_in).state));
                  #Run until the difference between current and standing height < stopping distance
            - switch.turn_off: raise_desk

number:
  - platform: template
    id: desk_target_height
    name: "Target Height x"
    optimistic: true
    unit_of_measurement: in
    min_value: 29
    max_value: 49
    step: 0.1

  - platform: template
    id: lower_limit
    name: "Lower Limit"
    optimistic: True
    unit_of_measurement: in
    min_value: 27
    max_value: 29
    step: 0.1
    restore_value: True
    initial_value: 28

  - platform: template
    id: upper_limit
    name: "Upper Limit"
    optimistic: True
    unit_of_measurement: in
    min_value: 48
    max_value: 50
    step: 0.1
    restore_value: True
    initial_value: 49
  
  - platform: template
    name: "Stopping Distance Offset"
    id: stopping_distance_in
    unit_of_measurement: in
    optimistic: true
    min_value: .1
    max_value: 1
    step: 0.1
    restore_value: true
    initial_value: .4

  - platform: template
    id: desk_standing_height
    name: "Standing Height"
    optimistic: true
    unit_of_measurement: in
    #Limit the range
    min_value: 45
    max_value: 49
    step: 0.1
    restore_value: true
    initial_value: 48

  - platform: template
    id: desk_sitting_height
    name: "Sitting Height"
    optimistic: true
    unit_of_measurement: in
    #Limit the range
    min_value: 28.5
    max_value: 32
    step: 0.1
    restore_value: true
    initial_value: 30

switch:
#wake up the desk and request it sends its height 
  - platform: gpio
    id: wake_desk_and_get_height
    name: "Request Desk Height"
    pin:
      number: 14
      inverted: true
    on_turn_on:
    - delay: 100ms
    - switch.turn_off: wake_desk_and_get_height

#Raise the desk 
  - platform: gpio
    id: raise_desk
    name: "Raise Desk"
    pin:
      number: 4
      # mode: INPUT_PULLUP
      inverted: true
    interlock: lower_desk
    on_turn_on:
    - wait_until:
     #Run until current > upper limit + stopping distance 
        condition:
          lambda: return (id(desk_height).state)>((id(upper_limit).state) - (id(stopping_distance_in).state));
    - switch.turn_off: raise_desk
#Lower the desk 
  - platform: gpio
    id: lower_desk
    name: "Lower Desk" 
    pin:
      number: 5
      # mode: INPUT_PULLUP
      inverted: true
    interlock: raise_desk
    on_turn_on:
    - wait_until:
     #Run until current < lower limit + stopping distance 
        condition:
          lambda: return (id(desk_height).state)<((id(lower_limit).state) + (id(stopping_distance_in).state));
   #Auto off after 15s just in case
    - switch.turn_off: lower_desk
1 Like