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

Does anyone know which standing desks are compatible? I have a workrite desk with a similar rj45 connector, so I would love to do this mod!

Did you read the first post?

1 Like

I’ve had good success using an RJ12 connection with my Omnidesk.

One quirk I discovered: the desk goes to sleep roughly 5 seconds after the last height message. If you send a command after this timeout, it gets ignored—so you’d need to send it twice to get a response.

To work around this, I’ve updated this on a fork of @Rocka84’s code to automatically send a “stop” message first if more than 4.5 seconds have passed since the last received message. This wakes the desk before sending any actual command.

I also noticed that near the physical limits, the desk can sometimes report inaccurate height values. For example, at the lowest position (63.7 cm), it occasionally returns something like 51.2 cm. So I’ve added logic to set any out-of-bounds readings to the nearest physical limit. The display seems to have similar logic as it shows the correct height (63.7cm) also.

I’m also interested to know how the bluetooth app is saving settings to the desk (i.e. custom heights and positions). I know it talks direct to the display as the controller has no bluetooth from what I can see? So I assume this is done via UART on the RJ45, but can’t see any TX pins in the original image. I have a logic analyser so would be great to find some more commands. If anyone has an idea of what pin the display is using for TX that would be great to know.

On the RJ45 port there are no uart messages from the control panel to the controller (only the other way).

Commands are sent via combining high/low on 4 wires. Take a look here around “known commands”.

On the rj12 port there is uart wires for both directions.

Hello, Let me start by thanking you and everyone involved for this awesome work! I tried to hold of before posting and I got pretty far, but alas I’m stuck.

I have a Uplift V1 desk with both an RJ12 and RJ45 connector. I picked up a WaveShare ESP32-S3-Zero-M and the appropriate dupont cables. Been a long time since I soldered and where I am, I can’t open the windows.

Initially I conflated which library to use with which diagram but I’ve got that also sorted using your main solution diagram and the default GitHub - ssieb/esphome_components: My collection of components for esphome firmware.

I’ve got the board powered by ethernet with no issue, and am not doing passthrough - for now I don’t see a need for it.

My issue lies in getting the current heigh back, no matter which combination I try, I don’t seem to get any information back.

I’ve tried using the TX/RX pins, using GPIO09 and GPIO08, and other combinations I’m too out of it to remember now. I currently have what would be your Blue ethernet position #4 connected to GPIO8 - though I’ve tried flipping 08 and 09 just to see.

The up/down/stop are working marvelously, is there a glaring mistake in the below, or is that a limitation of V1 Uplift?

esphome:
  name: desk-controller # change this!
  friendly_name: Desk Controller # change this!
  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: esp32-s3-devkitc-1
  framework:
    type: arduino

# Enable Home Assistant API
api:
  encryption:
    key: !secret ha_encrypt

ota:
  - platform: esphome
    password: !secret esp_home

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Deskcontroller Fallback Hotspot"
    password: !secret fb_pass

captive_portal:

logger:
  # level: VERY_VERBOSE ##Uncomment to see UART debug messages 

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/esphome_components
      ref: desky
    components: [ desky ]

uart:
  - id: desk_uart
    baud_rate: 9600
    #rx_pin: GPIO09
    tx_pin: GPIO09 #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

#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: 25.3
    max_value: 130.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: "Desky Standing Height"
    optimistic: true
    unit_of_measurement: cm
    #Limit the range
    min_value: 42.5
    max_value: 44.9
    step: 0.1
    restore_value: true
    initial_value: 43.9
    
#Sitting Height Chair - Set a sitting height. This is independant of what you've set via the control panel.
  - platform: template
    id: desky_sitting_height_chair
    name: "Desky Sitting Height Chair"
    optimistic: true
    unit_of_measurement: cm
    #Limit the range
    min_value: 26.5
    max_value: 27.8
    step: 0.1
    restore_value: true
    initial_value: 27
      
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: GPIO08
      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: GPIO12
      # 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: GPIO13
      # 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

Edit Here is a current wiring setup:

Thank you again, hoping to get automating soon!

uart:
  - id: desk_uart
    baud_rate: 9600
    rx_pin: GPIO09 # try some other pins too.
    debug:
      direction: RX
      dummy_receiver: true
      after:
        bytes: 4
      sequence:     
        - lambda: UARTDebug::log_int(direction, bytes, ',');

As a first step to debug, you should remove/comment out all of your config except for the very bare bones needed for uart and just focus on trying to recieve some uart debug messages on the wire that transmits height messages from the controller to the display.

This might be a brown wire as per diagram. Initially you may need to move the desk to trigger them to be sent. Usually debugging this step is just something simple with wiring, gpios or config.

If you find some uart messages in the logs then post them.

Hi, I have the same controller (jarvis desk), but mine does not work with the standard wiring (height is not read) - what wiring diagram did you use?

The post above yours may help troubleshoot.

Has anyone tried making HA talk to the new Desky bluetooth controller directly? I have a Bluetooth dongle used for Bluetooth integration in HA and I can see Desky bluetooth device in the advertisements.

Would be great to do the same what the phone app can do as it will not require any additional hardware.

Hi,

i have a desk with JCB36N2CA-230 and a simple up/down control unit mounted to the desk (Assmann TENSOS), i tried Rocka84’s project in the basic and full config but i can only use the step up/down controls via HA and i also do not get any sensor values.

Cables are connected to RX and TX of the Wemos D1 mini.
Any idea what could be wrong?

Is this possibly related to RX pin issue with Wemos D1 mini?: Desky Standing Desk (ESPHome) [Works with Desky, Uplift, Jiecang, Assmann & others] - #154 by android

And is here someone else with the exact same Control Unit JCB36N2CA-230
who has this already working and is willing to share his yaml?

Best Regards X23

Can you see any uart rx debug messages if you just focus on that wire?

You can try this config for debugging.

uart:
  tx_pin: GPIO21 # or other gpio 
  rx_pin: GPIO20 # or other gpio
  baud_rate: 9600
  debug:
    direction: RX
    dummy_receiver: true
    after:
      delimiter: [0x7E]

Hi,

@Mahko_Mahko Ok i soldered RX to GPIO5 and i got readings but this all i gained, the buttons step up/down still work, move up/down seem todo the same like step up/down which didnt worked before as far as i remember.

It read 3 of 4 saved positions around 62cm, i tried to change one, pressed preset button afterwards save position but it doesn‘t, maybe i am trying this the wrong way or it doens‘t work.

Still wondering that readings now work but not more buttons or functions in general.

Control Unit JCB36N2CA-230

Anything else i can do?
Maybe TX is also affected in some way even if the readings now work or is my yaml bad?

my yaml:

Summary

esphome:
name: jiecang-desk-controller
friendly_name: Jiecang Desk Controller
on_boot:
# This script is required to initialize the following sensors:
# height_pct, height_min, height_max, position1 - position4
# You can skip this if you don’t use those.
priority: 0 # when mostly everything else is done
then:
- lambda: “id(my_desk).request_physical_limits();”
- delay: 0.1s # give controller a chance to handle the response before sending the next command
- lambda: “id(my_desk).request_limits();”
- delay: 0.1s
- lambda: “id(my_desk).request_settings();”

external_components:

uart:
id: uart_bus
tx_pin: TX
rx_pin: GPIO5
baud_rate: 9600

logger:
baud_rate: 0 # disable logging over uart, required when using the RX/TX pins for the controller

jiecang_desk_controller:
id: my_desk
sensors:
height:
name: “Height”
height_min:
name: “Height Min”
height_max:
name: “Height Max”
height_pct:
name: “Height Percent”
position1:
name: “Position 1”
position2:
name: “Position 2”
position3:
name: “Position 3”
position4:
name: “Position 4”
buttons:
move_up:
name: “Move up”
move_down:
name: “Move down”
stop:
name: “Stop”
step_up:
name: “Step up”
step_down:
name: “Step down”
position1:
name: “Position 1”
position2:
name: “Position 2”
position3:
name: “Position 3”
position4:
name: “Position 4”
save_position:
name: “Save Position”
numbers:
height:
name: “Height”
height_pct:
name: “Height Percent”

the usual stuff

esp8266:
board: d1_mini

I tried the full config from rocka84, i had to correct the external_components part and remove the dublette of move up/down in his full config example.

I even can start debugging but will my last findings lead us further?
FYI my current desk control panel is just an up and down one without the special stuff, i just ordere the advanced one which is not here yet but i dont think it has anything todo with a physical control unit?

Should i try another board (esp32) i bought 3x wemos d1 clones (8266) from az delivery.

for the debugging part, i think i need to connect usb for serial console to read this right? is it no problem to have it powered twice, by RJ12 and micro USB?

Best Regards X23

No, you can just read the logs over WiFi. No need for usb.

What I would do is look at the uart messages being both sent and received by looking at the uart debug logs and check that they match the known commands and are not corrupted etc. That way you can also confirm your controller uses the expected protocol. Post some logs when you get them.

I would solder all wires, and potentially switch to an esp32 if I have one.

There is a decent chance you just need to sort out some gpios and wiring.

Some of the commands are here. They will also be in rocka84s project somewhere.

I think it is totally possible that the unit with only up/down buttons just does not support storing positions.

Totally guessing here but if I were them, I’d have one firmware source for all my desk units. That firmware could be configured (either on runtime or when compiling) to the features of the different units. For units that can’t store positions I’d just disable handling the “store” messages. But maybe the part that handles “request” messages would stay the same for all units - because it doesn’t matter to me or because I forgot about it.
That would explain why you can’t save positions but still get values - default values that is. But again, complete shot in the dark.

Hi,

thanks for your kind reply, well i wasn‘t aware that the function is maybe combined to the handset and its features. If so i will patiently wait and report back when the advanced unit arrived.

Best Regards X23

Does anyone sell these? My partner has a desk and I have a Varidesk and it would be awesome to add them to HomeAssistant.

I have two SLZB-06, i wonder if one of them could be used with ESPHome.

One person has reported success with a Varidesk.

Does your desk controller have both a rj12 and an rj45 port?

Maybe post a few pics of the controller ports and labels.

Hi,

I have an AlzaErgo ET1, it has an RJ12 port that I traced, and I would like to get it to work with my home assistant.

The RJ12 pinout is also weird
1 GND
2 5V
3 Unlabled, guess it’s either SWDIO/SWDCLK
4 SWD, no mention whether clock or data
5 TX
6 RX

Has anyone seen something like this before? It doesn’t look like a Jiecang product, no markings suggesting that other than Made In China. Also no smart connectivity.

If it helps, here’s the manual for the control panel. The control panel also drives the motors, there is only one unit.

Yeah that one looks quite different. You’re probably on your own with that.

Careful if you have some higher voltages on that mainboard.

Might be worth investigating these.

Oh yeah, I didn’t even see the thing in the right. Probably a USB A port, even has a dent in the plastic for it. The controller gets 12V DC from a power brick, so no dangerous voltages.
The four pins on the left are connected to the RJ12 ports TX/RX.


I’ll probably solder the connector I made to an ESP32 or something and try to spy on the bus.