Seit beginn der Corona Pandemie arbeite ich primär im Home Office und habe mir daher einen Schreibtisch gekauft der höhenverstellbar ist. Meine Wahl fiel auf einen Sanodesk EZ1. Hierzu hatte ich ein Video zum Aufbau erstellt, welches du dir hier ansehen kannst. Ich hatte mir nur das Gestell gekauft und darauf eine günstige Tischplatte von IKEA geschraubt, die ich bereits auf meinem vorherigen Schreibtisch genutzt hatte. In dieser Kombination war der Schreibtisch dank Rabattaktion sehr günstig mit knapp 230€.
Zu meiner Verwunderung war die Rechnung aber nicht von Sanodesk sondern von Flexispot! Somit war klar, dass Sanodesk einfach nur eine weitere Marke von Flexispot ist. Glücklicherweise habe ich mich damals für eine Premium Steuerung entschieden, die Funktionen wie Presets, Kindersicherung oder auch eine Hinderniserkennung beinhaltet.
Nun ist das Gestellt seit 2 Jahren im Einsatz und wird täglich mehrmals hoch und runter gefahren. Soweit war ich mit dem gebotenen und dem Funktionsumfang sehr zufrieden. Ein Youtube Zuschauer hatte mich zuletzt gefragt, ob die Steuerung einen USB Port besitzt um den Schreibtisch smart zu machen. Das hat meine neugier geweckt. Einen USB Port habe ich zwar nicht dafür aber zwei RJ45 Ports an der Control Box. Es gibt aber auch Control Panels mit USB Port. Ich habe mich kurzerhand an die Recherche gemacht und bin direkt fündig geworden!
In folgenden möchte ich dir nun zeigen, wie ich meinen Flexispot Schreibtisch samt Sprachsteuerung smart gemacht habe!
Loctek Motion Control Panel
Damit die hier gezeigte Lösung funktioniert benötigt es ein Control Panel von Loctek Motion. Ob eine Loctek Motion Steuerung verbaut ist lässt sich nur heraus finden indem das Control Panel aufgeschraubt wird. Bei meinem Panel ist es folgende Version: HS13A-2

Im Weiteren habe ich unter meinem Schreibtisch eine Control Box des Modells: CB28M1B(IB)
Die Control Box besitzt zwei RJ45 Anschlüsse. An einem Anschluss hängt das Control Panel. Der zweite Anschluss war also frei, um „etwas“ anzuschließen. Mit diesen Informationen habe ich mich nun auf die Suche begeben, um die richtige Vorgehensweise heraus zu finden, den Schreibtisch ins Smarthome zu integrieren. Auf der Projektseite gibt es 3 Methoden:
- Arduino
- ESP32
- Raspberry PI
Schnell habe ich mich für die ESPHome Variante entschieden und den Source Code von der Projektseite herunter geladen. Im Packages Order finden sich verschiedene vorgefertigte YAML Konfigurationen, die nur leicht angepasst werden müssen. Dazu aber später. Als erstes habe ich die Hardware vorbereitet. Alles was nötig ist hatte ich zu Hause.
- ESP8266
- RJ45 Kabel 0,25cm
- Dupont Kabel
- Wago Klemmen
Steuerung mit ESP8266 und RJ45 vorbereiten
Auf der einen Seite habe ich den Stecker des Netzwerkkabel abgeschnitten und die Drähte aufgetrennt. Anschließend abisoliert und verzinnt. Jetzt kam der schwierigste Part!
Die RJ45 Belegung des Netzwerkkabel muss auf den D1 mini gemappt werden als auch auf die Belegung des Loctek Motion Boards. Hierzu hilft es sehr die Belegung auf dem Board zu kennen als auch die Belegung des RJ45 Steckers. Auf dem Kabel sollte aufgedruckt sein, welcher Kabeltyp es ist. Mein Kabel ist vom Typ T-568B
Dan ich ein Control Panel ohne USB Anschluss habe ist auf meinem Board ein Stecker mit 8 Drähten. Bei Control Panles mit USB port sollten es ein Stecker mit 10 Drähten sein. Das folgende Schema ist das, welches bei mir funktioniert:


Mit Wago Klemmen habe ich anschließend die Verbindung zwischenESP8266 und RJ45 Kabel hergestellt. Es gibt auch Beispiele, bei denen die Drähte fest verlötet oder direkt auf das Loctek Board gelötet werden. Das wollte ich nicht und finde den Ansatz mit Wago Klemmen sehr flexibel. So lassen sich die Verbindungen bei Bedarf schnell lösen.

Was richtig cool ist, dass meine Control Box einen 5V Anschluss unterstützt. Damit bekommt der D1 Mini seinen Strom direkt über den RJ45 Anschluss von der Control Box. Ich musste also kein separates USB Kabel für den Strom ziehen.

ESP32 YAML Konfiguration
Technisch ist nun alles soweit vorbereitet. Als letztes habe ich die ESPHome Konfiguration erstellt auf Basis der Vorlage flexispot_ek5.yaml
In der Konfiguration, die vollständige YAML folgt weiter unten, habe ich einige wenige Anpassungen vorgenommen wie:
- Board eingestellt
- Wifi Credentials hinterlegt
- Virtual Screen angegeben sowie
- tx und rx Pin
Damit die Control Box Signale empfängt muss das Display aufgeweckt werden. Aufgeweckt ist das Display sobald die Höhe auf dem Display zu sehen ist. Über den WakeUp Schalter hat das zuerst nicht funktioniert. Daher muss folgende Zeile angepasst werden damit der D1 immer Befehle absetzen kann: restore_mode: ALWAYS_ON
switch:
- platform: gpio
name: "Virtual screen" # D2
pin:
number: ${screen_pin}
mode: OUTPUT
restore_mode: ALWAYS_ON
entity_category: "config"
internal: trueHome Assistant und Sprachsteuerung
Nachdem die ESPHome Konfiguration erstellt war und der ESP8266 Online, wird eine neues Gerät in Home Assistant erstellt. Über die Home Assistant eigene Sprachsteuerung Assist kann der Schreibtisch nun zusätzlich per Sprache gesteuert werden.

Fazit
Mit diesem Setup bieten sich nun diverse Möglichkeiten den Schreibtisch zu steuern. Ich hoffe dir hilft die Anleitung ebenfalls insofern du die entsprechenden Voraussetzungen hast und deinen Arbeitsplatz smart machen möchtest!
Weitere Artikel zum Thema ESP32 findest du hier!
Falls du noch Fragen haben solltest hinterlasse gerne einen Kommentar!
Den Inhalt des Beitrags findest du ebenfalls auf Youtube im zugehörigen Video:
Vollständiger Flexispot / LocTek Motion YAML Code:
substitutions:
device_name: Schreibtisch
name: schreibtisch
min_height: "73.5" # cm
max_height: "121" # cm
ssid: !secret wifi_ssid_cablenet_smart
wifi_password: !secret wifi_password_cablenet_smart
ap_fallback_password: "MnANX95MWad1"
tx_pin: D5 # TXD 2
rx_pin: D6 # RXD 2
screen_pin: D2
encryption_key: !secret esphome_encryption_key
external_components:
source: github://iMicknl/LoctekMotion_IoT
components: [ loctekmotion_desk_height ]
esp8266:
board: d1_mini
esphome:
name: ${name}
friendly_name: ${device_name}
comment: Konfiguration zur ${device_name} Steuerung mit Home Assistant.
# 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
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: ${encryption_key}
ota:
platform: esphome
wifi:
ssid: ${ssid}
password: ${wifi_password}
manual_ip:
static_ip: 192.168.2.125
gateway: 192.168.2.1
subnet: 255.255.255.0
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: ${device_name} Fallback Hotspot
password: ${ap_fallback_password}
captive_portal:
uart:
- id: desk_uart
baud_rate: 9600
tx_pin: ${tx_pin}
rx_pin: ${rx_pin}
# debug:
sensor:
- platform: wifi_signal
name: "WiFi signal"
update_interval: 60s
- 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" # D2
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

Moin Moin,
ich habe ein Gehäuse konstruiert für diesen smarten Umbau.
Anbei der Link zur freien Nutzung 🙂
https://makerworld.com/de/models/1315658-d1-mini-c3-rj-45-enclosure#profileId-1350708
Cool Danke für das Gehäuse!
Hallo Udo,
aufgrund Deines tollen Videos habe ich mir einen FlexiSpot Schreibtisch gekauft und wollte die ESP Steuerung einsetzen.
Bei Validieren des Files bekomme ich folgende Fehlermeldung:
The „custom“ component has been removed. Consider conversion to an external component.
https://esphome.io/guides/contributing#a-note-about-custom-components.
platform: custom
lambda: |-
auto desk_height_sensor = new DeskHeightSensor(id(desk_uart));
App.register_component(desk_height_sensor);
return {desk_height_sensor};
sensors:
id: desk_height
name: Desk Height
unit_of_measurement: cm
accuracy_decimals: 1
icon: mdi:counter
Ich bin noch neu in HA und konnte das Problem nicht lösen.
Hast Du eine vielleicht eine Lösung für dieses Problem?
Vielen Dank im Voraus.
VG Jesti
Hi Jesti,
die Meldung bekomme ich auch aber ich hatte noch keine Zeit mich um die Conversion zu kümmern. Aber der Schreibtisch lässt sich ja trotzdem steuern oder hast du Probleme mit der Steuerung?
VG, Udo
Nein Udo,
so weit komme ich ja gar nicht. Der Fehler taucht anscheinend seit ESP Home 2025.2 auf.
Hier einmal der Link, auf dem ich die Infos dazu gefunden habe.
https://community-smarthome.com/t/schreibtisch-smart-machen-von-flexispot-sanodesk-mit-loctek-motion-steuerung-per-esphome-esp8266/219/4
Hätte mich schon interessiert, wie eine ESPHome Installation mit einem Header-File funktioniert.
Vielleicht kannst Du Deinen Blog zu diesem Thema anpassen, damit weitere User nicht frustriert dieses
Projekt abhaken. Danke.
VG Jesti
Da ich keine Anpassungen oder Änderungen an meiner Konfiguration vorgenommen habe kann ich den Blogpost auch nicht updaten.
Aber hier ist die Basis zur Integration: https://github.com/iMicknl/LoctekMotion_IoT
Dort findest du aktualisierte YAML Dateien und passt diese auf deine Umgebung an.
VG, Udo