Yaasa Desk Expert – Schreibtisch Integration mit ESPHome & Home Assistant

Hallo zusammen,

ich habe meinen alten, Schreibtisch gegen einen Yaasa Desk Expert getauscht – und natürlich musste ich ihn direkt in Home Assistant einbinden!

Dank der RJ12-Schnittstelle am Schreibtisch-Controller konnte ich ihn mit ESPHome und meinem Home Assistant-Setup verbinden.

Ihr benötigt einen ESP32 und ein passendes RJ12 Stecker/Kabel.

ESPHome-Integration – Technische Umsetzung

Der Yaasa-Controller nutzt ein serielles Protokoll über UART. Ursprünglich wollte ich einen D1 Mini verwenden, hatte aber Probleme mit dem RX-Pin. Mit einem ESP32 lief alles problemlos. Die Steuerung funktioniert nun nicht nur über Home Assistant, sondern auch über mein Elgato Stream Deck! :sunglasses:

Das passende Youtube video findest du hier:

Hier mein ESPHome YAML-Code, falls ihr das nachbauen wollt, ich habe den Code von GitHub - Rocka84/esphome_components noch etwas erweitert:

esphome:
  name: yaasaeps32-test
  friendly_name: Yaasa Desk Expert
  on_boot:
    # don't touch if you don't know what you're doing!
    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:
  - source:
     #type: local
     # path: rocka84_esphome_components/components/
      type: git
      url: https://github.com/Rocka84/esphome_components/
    components: [ jiecang_desk_controller ]

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

bluetooth_proxy:
  active: false  
esp32_ble_tracker:
  id: ble_tracker
  scan_parameters:
    active: false # Whether to send scan-request packets to devices to gather more info (like devicename)
    interval: 211ms # suggested 211ms # default 320ms
    window:   120ms # suggested 120ms # default 30ms

jiecang_desk_controller:
  id: my_desk
  sensors:
    height:
      id: height
      name: "Height"
    height_pct:
      id: height_pct
      name: "Height Percent"  
    height_min:
      id: height_min
      name: "Height Min"
    height_max:
      name: "Height Max"

    position1:
      name: "Position 1"
    position2:
      name: "Position 2"
    position3:
      name: "Position 3"
    position4:
      name: "Position 4"
  buttons:
    move_up:
      name: "Move up"
      id: move_up
    move_down:
      name: "Move down"
      id: move_down
    stop:
      name: "Stop"
      id: stop
    step_up:
      name: "Step up"
      id: step_up
    step_down:
      name: "Step down"
      id: 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_pct:
      name: "Height Percent"
      id: height_percent_id
    height:
      name: "Height"
      id: height_id
      on_value: 
        then:
          - binary_sensor.template.publish:
              id: desk_is_moving
              state: True
          - delay: 100ms
          - binary_sensor.template.publish:
              id: desk_is_moving
              state: False

cover:

  - platform: template
    id: deskcover
    name: "Desk"
    icon: mdi:desk
    open_action:
      - button.press: move_up
    close_action:
      - button.press: move_down
   
    stop_action:
      - button.press: stop
    assumed_state: true
    optimistic: true
    has_position: true             
    lambda: |-
      if ( !isnan(id(height_pct).state) ) {
        float position = id(height_pct).state ;
        position = roundf(position ) / 100; // Round to two decimal places
        return position; }
 
    device_class: shutter


binary_sensor:
    # Didn't end up using this for the cover in the end.
  - platform: template
    id: desk_is_moving
    name: "Desk is Moving"
    filters:
      - delayed_off: 300ms  

button:

   - platform: template
     name: "Save Position 1"
     on_press:
       lambda: "id(my_desk).save_position(1);"   
   - platform: template
     name: "Save Position 2"
     on_press:
       lambda: "id(my_desk).save_position(2);"   
   - platform: template
     name: "Save Position 3"
     on_press:
       lambda: "id(my_desk).save_position(3);"   

   - platform: template
     name: "Save Position 4"
     on_press:
       lambda: "id(my_desk).save_position(4);"   

   - platform: template
     name: "Position 2"
     on_press:
       lambda: "id(my_desk).goto_position(2);"

   - platform: restart
     name: "Restart"
  
   - platform: template
     name: "Reminder"
     on_press:
      then:
        - button.press: step_up
        - delay: 1s
        - button.press: move_down

# the usual stuff

esp32:
  board: esp32dev
  framework:
    type: esp-idf
    version: recommended

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  ap:
    ssid: "esphome-desk"
    password: "passwort"

captive_portal:
api:
  encryption:
    key: "key"

  reboot_timeout: 0s

ota:
  platform: esphome
  password: "passwort"

web_server:
  port: 80

Ich habe alle Funktionen soweit es gingt in den ESPHome Code integriert.

Es mangelt aktuell nur an der Funktion die Position auch über Home Assistant über die Cover Entity zu steuern. Das wollte mit all meinen Versionen nicht funktionieren.
Daher ergänzt diese Automation noch das gesamte Setup. Wenn du noch eine Idee hast wie das zusätzlich in den ESPHome Code einfließen kann. Lass gerne noch etwas da:

alias: Yaasa Desk Cover Position
description: ""
triggers:
  - trigger: event
    event_type: call_service
    event_data:
      domain: cover
      service: set_cover_position
      service_data:
        entity_id: cover.yaasa_desk
conditions: []
actions:
  - action: number.set_value
    metadata: {}
    data:
      value: "{{trigger.event.data.service_data.position}}"
    target:
      entity_id: number.yaasaeps32_test_height_percent
mode: single
4 „Gefällt mir“

Vielen Dank Flo für deine Dokumentation. Ich habe es mit einem ESP32 probiert den ich zu Hause habe und er bekommt überhaupt keine Rückmeldung. Ich kann den Tisch nur über Height in cm steuern. Die Logs sagen die ganze Zeit die folgenden drei Begriffe: Current Operation: IDLE
‚Desk‘ - Publishing:
Position: nan%

Hat vielleicht jemand eine Ahnung wo mein Fehler liegt ?

Das scheinen tatsächlich Fehler aus dem Cover zu sein. Hast du meinen Code 1:1 übernommen?
Sind die jeweiligen IDs auch gleich?

Es scheint jetzt zu funktionieren. Kann es sein, dass man eine Art Kalibrierungsfahrt machen muss ? Die Werte und Funktionen haben sich dann Stück für Stück geladen je mehr ich den Schreibtisch hoch und runter gefahren habe.

1 „Gefällt mir“

Ah, ja er muss Werte empfangen damit die verschiedenen Variablen auch gefüllt werden. RX musste also mal Daten empfangen.
Schön, wenn es jetzt geht!