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

Yes. You basically just use/adapt/add this to trigger the memory presets, which use the soft start/stop.

thanks, so that I understand this. What do I add to trigger the memory presets?

Hereā€™s the relevant excerpt from my config. Note the Purple wire needs to connected to a GPIO.


substitutions:
  #Use your own ESP pin values
  desky_request_height_pin: GPIO32 #Request desk height | white wire  
  desky_purple_pin: GPIO04 #purple wire  
  desky_down_pin: GPIO23 #Move desk down | yellow wire  
  desky_up_pin: GPIO05  #Move desk up | green wire  

output:
  - platform: gpio
    pin: ${desky_up_pin}
    id: up_green_wire
    inverted: true
  - platform: gpio
    pin: ${desky_down_pin}
    id: down_yellow_wire
    inverted: true
  - platform: gpio
    pin: ${desky_purple_pin}
    id: purple_wire
    inverted: true

button:
  # Combination Buttons
  - platform: template
    name: 1
    id: button_1
    on_press:
      then:
        - output.turn_on: up_green_wire
        - output.turn_on: down_yellow_wire
        - delay: 300ms
        - output.turn_off: down_yellow_wire
        - output.turn_off: up_green_wire
  - platform: output
    output: purple_wire
    name: 2
    id: button_2
    duration: 300ms
  - platform: template
    name: 3
    id: button_3
    on_press:
      then:
        - output.turn_on: purple_wire
        - output.turn_on: down_yellow_wire
        - delay: 300ms
        - output.turn_off: down_yellow_wire
        - output.turn_off: purple_wire
  - platform: template
    name: 4
    id: button_4
    on_press:
      then:
        - output.turn_on: purple_wire
        - output.turn_on: up_green_wire
        - delay: 300ms
        - output.turn_off: up_green_wire
        - output.turn_off: purple_wire
    
  # - platform: template
    # name: Set memory 3
    # id: set_memory_3
    # on_press:
      # then:
        # - uart.write:
            # id: desk_uart
            # data: [0xF1, 0xF1, 0x25, 0x00, 0x25, 0x7E]
              # - uart.write: [0xF1, 0xF1, 0x25, 0x00, 0x25, 0x7E]

```

ok thanks!

So, at the moment, I have it wired as suggested above

image

Iā€™ll have to add an extra wire from the purple pin (or slot 3 on the above figure) of the adapter and place it on any of the empty pins on the D1 Mini?

Thatā€™s right.

Pick a pin that has ā€œokā€ in both the Input and Output columns here.

Best Pins to Use ā€“ ESP8266

Awesome! Just did the addition and works very well.

I can now access my button presets and its a soft stop.

This project just keeps getting better and better. thanks again, all.

EDIT: @Mahko_Mahko one thing Iā€™ve noticed is when the D1 board restarts, it goes to one of my presets (i.e. preset #2 in my case). My only concern with this is that on every update or even when the board restarts for whatever reason, it will go to a preset on its own. Is this an intended operation?

Hereā€™s my entire yaml if there is something that is not right.

esphome:
  name: esphome-web-5bdf28   
  on_boot:
    priority: -100.0
    then:
    #Request a desk height update after boot.
      - delay: 5s
      - switch.turn_on: wake_desk_and_get_height

substitutions:
  #Use your own ESP pin values
  desky_request_height_pin: GPIO14 #Request desk height | white wire  
  desky_purple_pin: GPIO12 #purple wire  
  desky_down_pin: GPIO05 #Move desk down | yellow wire  
  desky_up_pin: GPIO04  #Move desk up | green wire  

output:
  - platform: gpio
    pin: ${desky_up_pin}
    id: up_green_wire
    inverted: true
  - platform: gpio
    pin: ${desky_down_pin}
    id: down_yellow_wire
    inverted: true
  - platform: gpio
    pin: ${desky_purple_pin}
    id: purple_wire
    inverted: true

esp8266:
  board: d1_mini

# Enable logging
logger:

# Enable Home Assistant API
api:

ota:

## For Memory Sensor (debug platform in sensor)
debug:
  update_interval: 30s

sensor:
  # Sensors
  - platform: uptime
    name: "Desky Uptime"
  - platform: wifi_signal
    name: "Desky WiFi Signal"
    update_interval: 60s
  - platform: debug
    free:
      name: "Heap Free"
    fragmentation:
      name: "Heap Fragmentation"
    block:
      name: "Heap Max Block"
    loop_time:
      name: "Loop Time"

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "[redacted]"
    password: "[redacted]"

captive_portal:

####################################################################################################
##Notes/Info
####################################################################################################

## Home Assistant Thread: https://community.home-assistant.io/t/desky-standing-desk/383790/3
## ssieb who generously donated his time and knowledge to developing the custom component may offer some support on the Discord thread (please read both threads first).
## https://discord.com/channels/429907082951524364/952464939480645642

#Notes:
# 1. Use this component at own risk. It has not had extensive testing. May void warranty or cause desk faults etc.
# 2. This controller may bypass desk safety features like collision detection.
# 3. This component doesn't know about the values stored in your desk. Values in this config are (and must be) set independently.
# 4. ssieb's solution may be a lot simpler and cleaner for most people. I've cobbled together some more features in this config because I can't programn in C++.


#Troubleshooting Tips: 
#If you ever get a flashing ASr message, you might need to reset your desk.
#https://desky.com.au/blogs/news/reset-standing-desk-control-panel#:~:text=When%20ready%2C%20press%20and%20hold,Hooray!


#TODO 
#Ask ssieb to make change requests (move logic to component code)
  # Invert pins
  # Get Desk Height On Boot
  # Interlock Up/Down Pins (If not done already)
  # Interrupt/stop movement function (Stop desk motion)

#other
  # Test collision detection
  # Limit external component fetch to just desky? 
  # Fix Desk Is moving on Boot.

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 #Labelled TX on my D1mini clone (mislabelled?)
    ##You can uncomment the debug section below to see UART messages.
    # debug:
      # direction: RX
      # dummy_receiver: true
      # after:
        # bytes: 4
      # sequence:     
        # - lambda: UARTDebug::log_int(direction, bytes, ',');
 
desky:
  id: my_desky
  ####################################################################################
  ##Uncomment this block to use Ssieb's move_to componet function.
  # up:    
    # number: 4 #D2
    # inverted: true 
  # down:  
    # number: 5 #D1
    # inverted: true
  # stopping_distance: 15  # optional distance from target to turn off moving, default 15  
  ####################################################################################
  height:  # Sensor publishing the current height
    name: Desky Height
    id: desky_height
    accuracy_decimals: 1
    unit_of_measurement: cm
    #any other sensor options
    filters:
    - delta: 0.05 #Only send values to HA if they change
    - throttle: 200ms #Limit values sent to Ha to 5 per sec.
    - multiply: 0.1 #convert from mm to cm
    on_value:
      then:
          #If the value changes, then the desk is moving
        - binary_sensor.template.publish:
            id: desky_is_moving
            state: ON
        - delay: 300ms
          #Assume it's stopped moving if no height changes after a short time.
        - binary_sensor.template.publish:
            id: desky_is_moving
            state: Off
            
binary_sensor:
  - platform: template
    id: desky_is_moving
    name: "Desky Is Moving"
    filters:
      - delayed_off: 400ms
    #If the desk isn't moving for a bit we better turn off attempts at movement. It's like 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
# Combination Buttons
  - platform: template
    name: Desky Button 1
    id: button_1
    on_press:
      then:
        - output.turn_on: up_green_wire
        - output.turn_on: down_yellow_wire
        - delay: 300ms
        - output.turn_off: down_yellow_wire
        - output.turn_off: up_green_wire
  - platform: output
    output: purple_wire
    name: Desky Button 2
    id: button_2
    duration: 300ms
  - platform: template
    name: Desky Button 3
    id: button_3
    on_press:
      then:
        - output.turn_on: purple_wire
        - output.turn_on: down_yellow_wire
        - delay: 300ms
        - output.turn_off: down_yellow_wire
        - output.turn_off: purple_wire
  - platform: template
    name: Desky Button 4
    id: button_4
    on_press:
      then:
        - output.turn_on: purple_wire
        - output.turn_on: up_green_wire
        - delay: 300ms
        - output.turn_off: up_green_wire
        - output.turn_off: purple_wire

#Move to function
  - platform: template
    name: Go To Desky Height x
    id: go_to_desky_preset_height_x
    on_press:
      then:
      ##Option 1: Uncomment to use Ssieb's move_to componet functions
        # - lambda: id(my_desky).move_to(id(desky_target_height).state*10);  

        
      ##Option 2: Uncomment to use Mahko's lambda alternative 
      #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(desky_target_height).state < id(desky_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 abs((id(desky_height).state - (id(desky_target_height).state)))<(id(stopping_distance_cm).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 abs((id(desky_height).state - (id(desky_target_height).state)))<(id(stopping_distance_cm).state);
                  #Run until the difference between current and target state is <0.3cm
            - switch.turn_off: raise_desk


number:
#Target Height ("Move desk to height x").
    #You should probably limit the range you can move the desk to to within the limits you've set via the control panel, perhaps offset a little within the range.
    #Sending commands higher/lower than this may cause error messages and require desk reset (or worse).
  - platform: template
    id: desky_target_height
    name: "Desky Target Height"
    optimistic: true
    unit_of_measurement: cm
    min_value: 65.0 
    max_value: 122.0
    step: 0.1
    
#Offset correction - Adjust until you get the best accuracy. 
#The desk keeps moving for a little while after the up/down pins are released and we try to account for this.
#1.5cm was about right on mine
  - platform: template
    name: "Desky Stopping Distance cm"
    id: stopping_distance_cm
    unit_of_measurement: cm
    optimistic: true
    min_value: 0
    max_value: 2
    step: 0.1
    restore_value: true
    initial_value: 1.5
    
###############################################
#Define some preset heights.
###############################################
#You can freely define as many adjustable presets as you like.
#These are all seperate/independant of what you've set via the control panel (we can't retrieve them currently).
 
#Standing Height #1 - Set a standing height. 
  - platform: template
    id: desky_standing_height_1
    name: "Standing Height 1"
    optimistic: true
    unit_of_measurement: cm
    #Limit the range
    min_value: 110
    max_value: 120
    step: 0.1
    restore_value: true
    initial_value: 115

#Standing Height #1 - Set a standing height. 
  - platform: template
    id: desky_standing_height_2
    name: "Standing Height 2"
    optimistic: true
    unit_of_measurement: cm
    #Limit the range
    min_value: 115
    max_value: 125
    step: 0.1
    restore_value: true
    initial_value: 120
  
#Sitting Height #1 - Set a sitting height. This is independant of what you've set via the control panel.
  - platform: template
    id: desky_sitting_height_1
    name: "Sitting Height 1"
    optimistic: true
    unit_of_measurement: cm
    #Limit the range
    min_value: 65
    max_value: 69
    step: 0.1
    restore_value: true
    initial_value: 67.0

#Sitting Height #2 - Set a sitting height. This is independant of what you've set via the control panel.
  - platform: template
    id: desky_sitting_height_2
    name: "Sitting Height 2"
    optimistic: true
    unit_of_measurement: cm
    #Limit the range
    min_value: 72.6
    max_value: 82.6
    step: 0.1
    restore_value: true
    initial_value: 77.6
      
switch:
#wake up ther desk and request it sends its height 
  - platform: gpio
    id: wake_desk_and_get_height
    name: "Request Desk Height"
    pin:
      number: D5
      inverted: true
    on_turn_on:
    - delay: 100ms
    - switch.turn_off: wake_desk_and_get_height
  - platform: restart
    name: "Desky Restart"

#Raise the desk 
  - platform: gpio
    id: raise_desk
    name: "Raise Desk"
    pin:
      number: D2
      # mode: INPUT_PULLUP
      inverted: true
    interlock: lower_desk
    on_turn_on:
    #Auto off after 15s just in case
    - delay: 15s
    - switch.turn_off: raise_desk
#Lower the desk 
  - platform: gpio
    id: lower_desk
    name: "Lower Desk" 
    pin:
      number: D1
      # mode: INPUT_PULLUP
      inverted: true
    interlock: raise_desk
    on_turn_on:
   #Auto off after 15s just in case
    - delay: 15s
    - switch.turn_off: lower_desk

I suspect it has something to do with this entry:

- platform: output
  output: purple_wire
  name: Desky Button 2
  id: button_2
  duration: 300ms

If this is the case, how do I make it such that its like Button 1, 3 and 4 and only command on button press. Thanks

Hmm yeah thatā€™s no good.

Sounds like something is pulling the pin low on boot I think.

Maybe try another pin or two (rather than GPIO12) as a next step.

ok thanks for that.

I tried GPIO13, and it did the same thing (when board restarts, it goes to preset 2).

I donā€™t really have any other options as the other D1 and D2 are already used. May I ask why preset 2 is different in terms of yaml configuration? can the code be change to on_press instead?

EDIT: actually, the on_press may not solve the issue. I actually completely hashed out button 2 (using #) and after restart, it still went to preset 2.

1 Like

The config for that button can be simpler/more direct for that wire as it only uses one wire.

But it shouldnā€™t be triggered on boot. Mine doesnā€™t.

I donā€™t know whatā€™s triggering it and am somewhat out of ideas.

Maybe try activating pullup or pulldown resistors for the pin?

And separately maybe try commenting out the existing on_boot code and see if that changes anything.

And triple check your wiring for that pin.

Also, I recall seeing similar issues on this forum ā€œthing X turns on/off momentarily on restart/ bootā€. So maybe dig around to see if you can find a similar issue/solution.

Definitely look at early_pin_init

why you, sir, are a genius @Mahko_Mahko.

I read through all your suggestions but the last one made more sense so I started that first. I set early_pin_init: false and after the board updated and came back onlineā€¦no desk movement, and even when I rebooted the board manually, no desk movement! problem is solved! thank you

1 Like

hi, I have a desk from a manufacturer where the motor control box has two RJ-45 ports. My question is - can I plug the DIY remote controller in the second RJ-45 slot or do I have to create a passthrough box - so that the original physical controller and the DIY one are on the same connection?

Is it a Jiecang based desk?

hello, it says manufacturer: Loctek ergonomic technology corp.

Then you are probably better off over here.

Where I can see youā€™ve asked the same questionā€¦

Yeah, however, did not hey any response there yet and since then I realized that I actually have the motor from the same vendor. Anyway, my question was a bit generic - if having two rj45 ports pass through is the only way.

Thanks, anyway.

So I got this working a while ago and itā€™s been amazing. Iā€™m trying to integrate it with some other stuff, and now Iā€™d like to trigger automations based on the direction the desk is moving.

Is there a way to tell? The only way I can think of doing that is comparing the current height a second ago, but I have no clue how to do that.

1 Like

If you are moving the desk using the move_to commandā€¦

on_...:
  then:
    - lambda: id(my_desky).move_to(150);

Then you can make use of the DESKY_OPERATION feature that Ssieb kindly built at my request. Also see this bit. They were inspired by thier cover equivalents (COVER_OPERATION_OPENING, COVER_OPERATION_CLOSING)

How ever this feature wonā€™t report direction if you use the desks native contoller or trigger the memory buttons via ESPHome.

I recently worked around this by basically doing what youā€™ve suggested, which is watch for changes in height.

My implementation is bascially a ā€œdelta sensorā€. Thereā€™s probably a better way to do this but itā€™s ok for my purpose for the moment.

Make sense?

  - platform: copy
    source_id: desky_height_cm
    id: desky_height_cm_delta
    name: "Desky Desk Height Delta"
    accuracy_decimals: 1
    internal: true
    icon: mdi:arrow-expand-vertical
    unit_of_measurement: cm
    filters:
       #Convert to Delta
      - lambda: |-
          static float last_value = 0;
          float  change = 0;
          change = x - last_value;
          last_value = x;
          return change;
    on_value:
      then:
        - lambda: |-
            if ( x > 0){ return id(desky_last_movement_direction).publish_state("Up");}
            else if (x < 0){return id(desky_last_movement_direction).publish_state("Down"); }
        - lambda: |-
            if (id(desky_height_cm_delta).state != 0) {
                id(desky_is_moving).publish_state(true);
            }
        - delay: 200ms
        - lambda: return id(desky_is_moving).publish_state(false);

binary_sensor:
  - platform: template
    id: desky_is_moving
    name: Desky Is Moving
    # lambda: return id(my_desky).current_operation != desky::DESKY_OPERATION_IDLE;
    icon: "mdi:motion"
    filters:
      - delayed_off: 1s

Interesting, Iā€™ll give it a try, thanks!

1 Like

Can anyone report success with a MAIDeSITe desk?