LoctekMotion_IoT
LoctekMotion_IoT copied to clipboard
Add support for Flexispot ED5
Hello,
I am currently trying to make my Flexispot ED5 smart with the help of an ESP8266 and this repo.
The ED5 comes with the following control box:
And the following keypad:
I tried to adapt the wiring from this https://github.com/iMicknl/LoctekMotion_IoT/tree/main/packages/esphome#hs01b-1-with-esp32-as-passthrough-and-keypad-support
as follows:
I adapted the yaml file from https://github.com/iMicknl/LoctekMotion_IoT/blob/main/packages/esphome/flexispot_e5b_esp32.yaml and https://github.com/iMicknl/LoctekMotion_IoT/blob/main/packages/esphome/flexispot_e5b.yaml and it looks like this:
substitutions: device_name: Flexispot ED5 name: flexispot_ed5 min_height: "64.6" # Min height + 0.1 max_height: "129.9" # Max height - 0.1 esphome: name: ${name} comment: ${device_name} platform: ESP8266 # TODO Change to your platform board: nodemcuv2 # TODO Change to your board includes: - desk_height_sensor.h - desk_keypad.h wifi: ssid: !secret wifi_ssid password: !secret wifi_password # Enable fallback hotspot (captive portal) in case wifi connection fails #ap: #ssid: "${device_name} Fallback Hotspot" #password: !secret ap_fallback_password captive_portal: # Enable logging logger: level: DEBUG baud_rate: 0 # Enable Home Assistant API api: password: !secret ha_api_password ota: platform: esphome #password: !secret ha_api_password script: - id: screen_timer mode: restart then: # on_start - logger.log: "Executing screen timer command" - switch.turn_on: virtual_screen - delay: 12s # timer length (keypad screen timeout is 10s + 2s buffer) - if: condition: binary_sensor.is_off: screen then: - switch.turn_off: virtual_screen # on_end - id: script_start_command mode: restart then: - logger.log: "Executing Empty command" - if: condition: switch.is_off: virtual_screen then: - script.execute: screen_timer - delay: 480ms else: - script.execute: screen_timer uart: - id: desk_uart baud_rate: 9600 tx_pin: TX rx_pin: RX - id: keypad_uart baud_rate: 9600 tx_pin: D6 # Why do I have to configure the TX pin here when it says here (https://github.com/iMicknl/LoctekMotion_IoT/tree/main/packages/esphome#hs01b-1-with-esp32-as-passthrough-and-keypad-support) "esp32: no pin, keypad: RX, bridged to RX from controller"? rx_pin: D5 # Used as second RX pin. Is this correct? sensor: - platform: wifi_signal name: "WiFi Signal" update_interval: 60s - platform: uptime name: Uptime - 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" on_value: then: - script.execute: screen_timer # - cover.template.publish: # id: desk # 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; - platform: custom lambda: |- auto desk_keypad_sensor = new DeskKeypad(id(keypad_uart)); App.register_component(desk_keypad_sensor); return {desk_keypad_sensor}; sensors: id: "desk_command" name: Desk command icon: "mdi:counter" on_value: then: lambda: |- if(!id(keypad_switch).state) { if(id(desk_command).state == 1) { id(desk).open(); } else if(id(desk_command).state == 2) { id(desk).close(); } else if(id(desk_command).state == 3) { id(switch_preset1).turn_on(); } else if(id(desk_command).state == 4) { id(switch_preset2).turn_on(); } else if(id(desk_command).state == 5) { id(switch_preset3).turn_on(); } else if(id(desk_command).state == 6) { id(switch_m).turn_on(); } else if(id(desk_command).state == 7) { id(switch_alarm).turn_on(); } else if(id(desk_command).state == 8) { id(desk).stop(); } } binary_sensor: - platform: gpio name: "Screen" id: "screen" pin: D1 internal: false on_press: then: - switch.turn_on: virtual_screen on_release: then: - switch.turn_off: virtual_screen switch: - platform: template name: "Keypad locked" icon: mdi:key id: "keypad_switch" internal: false #restore_state: true restore_mode: RESTORE_DEFAULT_ON assumed_state: false optimistic: true - platform: template name: "Preset 1" id: switch_preset1 icon: mdi:numeric-1-box turn_on_action: - logger.log: "Executing Preset 1 command" - script.execute: script_start_command - script.wait: script_start_command - uart.write: id: desk_uart data: [0x9b, 0x06, 0x02, 0x04, 0x00, 0xac, 0xa3, 0x9d] internal: false - platform: template name: "Preset 2" id: switch_preset2 icon: mdi:numeric-2-box turn_on_action: - logger.log: "Executing Preset 2 command" - script.execute: script_start_command - script.wait: script_start_command - uart.write: id: desk_uart data: [0x9b, 0x06, 0x02, 0x08, 0x00, 0xac, 0xa6, 0x9d] internal: false - platform: template name: "Preset 3" id: switch_preset3 icon: mdi:numeric-3-box turn_on_action: - logger.log: "Executing Preset 3 command" - script.execute: script_start_command - script.wait: script_start_command - uart.write: id: desk_uart data: [0x9b, 0x06, 0x02, 0x10, 0x00, 0xac, 0xac, 0x9d] internal: false - platform: template name: "M" id: switch_m icon: mdi:alpha-m-circle turn_on_action: - logger.log: "Executing Preset 3 command" - script.execute: script_start_command - script.wait: script_start_command - uart.write: id: desk_uart data: [0x9b, 0x06, 0x02, 0x20, 0x00, 0xac, 0xb8, 0x9d] internal: false - platform: template name: "Alarm" id: switch_alarm icon: mdi:alpha-m-circle turn_on_action: - logger.log: "Executing Alarm command" # - script.execute: script_start_command # - script.wait: script_start_command # - uart.write: # id: desk_uart # data: [0x9b, 0x06, 0x02, 0x40, 0x00, 0xac, 0x90, 0x9d] internal: false - platform: gpio name: "Virtual Screen" id: "virtual_screen" pin: number: D2 mode: OUTPUT restore_mode: ALWAYS_OFF internal: false - 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 internal: false - 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 internal: false - platform: uart name: "Empty command" id: switch_empty data: [0x9b, 0x06, 0x02, 0x00, 0x00, 0x6c, 0xa1, 0x9d] uart_id: desk_uart internal: false cover: - platform: template name: "Desk" id: "desk" assumed_state: true # has_position: true # position_action: # - logger.log: "Requesting action change" # Move desk up open_action: - script.execute: script_start_command - script.wait: script_start_command - while: condition: sensor.in_range: id: desk_height below: ${max_height} then: - logger.log: "Executing up command" # - cover.template.publish: # id: desk # current_operation: OPENING - switch.turn_on: switch_up - delay: 108ms # Move desk down close_action: - script.execute: script_start_command - script.wait: script_start_command - while: condition: sensor.in_range: id: desk_height above: ${min_height} then: - logger.log: "Executing down command" # - cover.template.publish: # id: desk # current_operation: CLOSING - switch.turn_on: switch_down - delay: 108ms optimistic: true
I have two questions about the yaml file:
1: Why do I have to configure the TX pin for the keypad if it says here (https://github.com/iMicknl/LoctekMotion_IoT/tree/main/packages/esphome#hs01b-1-with-esp32-as-passthrough-and-keypad-support): "esp32: no pin, keypad: RX, bridged to RX from controller"? So I used the unused D6 as a dummy... This is probably completely stupid.
2: My ESP8266 has only one TX and one RX (I think it's normal?), so I use D5 as a second RX pin for keypad. Is this correct?
I was able to successfully flash my ESP8266 via ESPHome with my configuration, but nothing works and I probably fried my ESP8266. I suspect that the wiring is not correct. Here is a more detailed overview of the wiring:
maybe this can help? Get out the oscilloscope
https://youtu.be/1IRBaRmFxDU?si=rlBDDUVq_YBBJawE
You should find the model of the keypad somewhere on the PCB. Otherwise you can trace the wires using a continuity tester and follow the "passthrough" instructions in the v1 archive: https://github.com/iMicknl/LoctekMotion_IoT/tree/main/archive/esphome.
Your ESP8266 also needs power. You can use the 5V from the desk if you connect it to the 5V input on your ESP8266 dev board.
You also need to bridge all of the other connections between the desk and the keypad.
maybe this can help? Get out the oscilloscope
https://youtu.be/1IRBaRmFxDU?si=rlBDDUVq_YBBJawE
I have also subscribed to the channel, but haven't seen the video yet. I will definitely watch it. Thank you.
You should find the model of the keypad somewhere on the PCB.
It's a little hard to see because there's this circular spring thing above the writing. It's something like "HS13U-...".
Otherwise you can trace the wires using a continuity tester
How does that work?
and follow the "passthrough" instructions in the v1 archive: https://github.com/iMicknl/LoctekMotion_IoT/tree/main/archive/esphome.
On the page I can only find the wiring for Loctek Motion (HS13A-1) Loctek Motion (HS01B-1). This will probably be completely different for me?
@amroncz Did you get this working in the end? I have a different control panel but the same control box as you. I've worked out the pinout and managed to capture all the codes for the buttons. Interestingly I get a different pin out for the RJ45 to the one in your wiring diagram:
8 -> 5v 7 -> GND 6 -> RX 5 -> TX 4 -> Pin20
I was hoping to just define a second UART and echo the traffic across them but the current code fails to compile with a second UART. Going to spend this weekend looking at either modifying the existing custom component to allow 2 UARTs or implementing the v1 code. I thought I'd find out if you managed to get it working?
@amroncz Did you get this working in the end? I have a different control panel but the same control box as you. I've worked out the pinout and managed to capture all the codes for the buttons. Interestingly I get a different pin out for the RJ45 to the one in your wiring diagram:
8 -> 5v 7 -> GND 6 -> RX 5 -> TX 4 -> Pin20
I was hoping to just define a second UART and echo the traffic across them but the current code fails to compile with a second UART. Going to spend this weekend looking at either modifying the existing custom component to allow 2 UARTs or implementing the v1 code. I thought I'd find out if you managed to get it working?
Unfortunately, I still haven't managed to do it. After I fried 3 of my ESPs and got no help here or in the Discord, I put the project on hold for the time being. I'll probably have to move on to simpler projects of this kind first, as I'm a complete noob. If you find out anything, please let me know anyway.
With the help of Yama from the Discord, I've made a lot of progress. My wiring currently looks like this:
And this is my code:
substitutions:
min_height: "60" # cm
max_height: "120" # cm
c_screen_pin: D2
c_tx_pin: D5
c_rx_pin: D6
k_screen_pin: D7
k_tx_pin: D0
k_rx_pin: D1 # unused
friendly_name: "Flexispot ED5"
name: "flexispot-ed5"
esphome:
name: ${name}
friendly_name: ${friendly_name}
includes:
- desk_height_sensor.h
- desk_keypad.h
on_boot:
priority: -10
then:
- switch.turn_on: switch_wake_up
external_components:
source: github://iMicknl/LoctekMotion_IoT
components: [loctekmotion_desk_height]
logger:
level: DEBUG
baud_rate: 0
esp8266:
board: nodemcuv2
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
manual_ip:
static_ip: 192.168.2.149
gateway: 192.168.2.1
subnet: 255.255.255.0
ap:
ssid: "recover me ${friendly_name}"
captive_portal:
api:
password: !secret ha_api_password
ota:
- platform: esphome
uart:
- id: desk_uart
baud_rate: 9600
tx_pin: ${c_tx_pin}
rx_pin: ${c_rx_pin}
- id: keypad_uart
baud_rate: 9600
tx_pin: ${k_tx_pin}
rx_pin: ${k_rx_pin}
script:
- id: screen_timer
mode: restart
then:
# on_start
- logger.log: "Executing screen timer command"
- switch.turn_on: virtual_screen
- delay: 12s # timer length (keypad screen timeout is 10s + 2s buffer)
- if:
condition:
binary_sensor.is_off: screen
then:
- switch.turn_off: virtual_screen
# on_end
- id: script_start_command
mode: restart
then:
- logger.log: "Executing Empty command"
- if:
condition:
switch.is_off: virtual_screen
then:
- script.execute: screen_timer
- delay: 480ms
else:
- script.execute: screen_timer
binary_sensor:
- platform: gpio
name: "Screen"
id: "screen"
pin: ${k_screen_pin}
internal: false
on_press:
then:
- switch.turn_on: virtual_screen
on_release:
then:
- switch.turn_off: virtual_screen
sensor:
- platform: wifi_signal
name: "WiFi Signal"
update_interval: 60s
- platform: uptime
name: Uptime
- platform: loctekmotion_desk_height
id: "desk_height"
name: Desk Height
uart_id: desk_uart
filters:
- clamp:
min_value: ${min_height}
max_value: ${max_height}
ignore_out_of_range: true
- debounce: 10ms
on_value_range:
- below: ${min_height}
then:
- switch.turn_off: switch_down
- above: ${max_height}
then:
- switch.turn_off: switch_up
on_value:
then:
- script.execute: screen_timer
- component.update: set_desk_height
- platform: custom
lambda: |-
auto desk_keypad_sensor = new DeskKeypad(id(keypad_uart));
App.register_component(desk_keypad_sensor);
return {desk_keypad_sensor};
sensors:
id: "desk_command"
name: Desk command
icon: "mdi:counter"
on_value:
then:
lambda: |-
if(id(desk_command).state == 1)
{
id(button_up).press();
} else if(id(desk_command).state == 2)
{
id(button_down).press();
} else if(id(desk_command).state == 3)
{
id(button_stand).press();
} else if(id(desk_command).state == 4)
{
id(button_sit).press();
} else if(id(desk_command).state == 5)
{
id(button_3).press();
} else if(id(desk_command).state == 6)
{
id(button_4).press();
} else if(id(desk_command).state == 7)
{
id(button_m).press();
} else if(id(desk_command).state == 8)
{
id(desk_cover).make_call().set_command_stop().perform();
}
button:
- platform: uart
name: "Step Up"
id: button_up
icon: mdi:arrow-up-bold
data: [0x9b, 0x06, 0x02, 0x01, 0x00, 0xfc, 0xa0, 0x9d]
uart_id: desk_uart
- platform: uart
name: "Step Down"
id: button_down
icon: mdi:arrow-down-bold
data: [0x9b, 0x06, 0x02, 0x02, 0x00, 0x0c, 0xa0, 0x9d]
uart_id: desk_uart
- platform: template
name: "Stand"
icon: mdi:numeric-1-box
id: button_stand
on_press:
- uart.write:
id: desk_uart
data: [0x9b, 0x06, 0x02, 0x04, 0x00, 0xac, 0xa3, 0x9d]
- platform: template
name: "Sit"
icon: mdi:numeric-2-box
id: button_sit
on_press:
- uart.write:
id: desk_uart
data: [0x9b, 0x06, 0x02, 0x08, 0x00, 0xac, 0xa6, 0x9d]
- platform: template
name: "Preset 3"
icon: mdi:chair-rolling
id: button_3
on_press:
- uart.write:
id: desk_uart
data: [0x9b, 0x06, 0x02, 0x00, 0x01, 0xac, 0x60, 0x9d]
- platform: template
name: "Preset 4" # Preset 4 on some control panels
icon: mdi:human-handsup
id: button_4
on_press:
- 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]
switch:
- platform: gpio
name: "Virtual Screen"
id: "virtual_screen"
pin:
number: ${c_screen_pin}
mode: OUTPUT
restore_mode: ALWAYS_ON
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: 1ms
on_turn_on:
then:
- switch.turn_off: switch_down
internal: true
- 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: 1ms
on_turn_on:
then:
- switch.turn_off: switch_up
internal: true
- platform: uart
name: "(wake up)" # Not available on all control panels
id: switch_wake_up
icon: mdi:gesture-tap-button
data: [0x9b, 0x06, 0x02, 0x00, 0x00, 0x6c, 0xa1, 0x9d]
uart_id: desk_uart
internal: true
- platform: uart
name: "Empty command"
id: switch_empty
data: [0x9b, 0x06, 0x02, 0x00, 0x00, 0x6c, 0xa1, 0x9d]
uart_id: desk_uart
internal: false
cover:
#- platform: template
# icon: mdi:table-chair
# # icon: mdi-human-male-height-variant
# name: "Desk"
# id: "desk"
# assumed_state: true
# has_position: true
# # set current position percent
# lambda: return (float(id(desk_height).state) - float(62.1)) / (float(126.9) - float(62.1));
- platform: template
id: desk_cover
icon: mdi:table-chair
name: "Desk"
assumed_state: true
optimistic: true
# Move desk up
open_action:
- if:
condition:
- switch.is_on: switch_down
then:
- switch.turn_off: switch_down
- delay: 100ms
- switch.turn_on: switch_up
# Move desk down
close_action:
- if:
condition:
- switch.is_on: switch_up
then:
- switch.turn_off: switch_up
- delay: 100ms
- switch.turn_on: switch_down
stop_action:
then:
- switch.turn_off: switch_down
- switch.turn_off: switch_up
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:
# direction up
- cover.open: desk_cover
- wait_until:
lambda: |-
return id(desk_height).state >= x - 1;
- cover.stop: desk_cover
- delay: 1s
# move a little down again to adjust for momentum
- uart.write:
id: desk_uart
data: [0x9b, 0x06, 0x02, 0x02, 0x00, 0x0c, 0xa0, 0x9d] # down
else:
# direction down
- cover.close: desk_cover
- wait_until:
lambda: |-
return id(desk_height).state <= x + 3;
- cover.stop: desk_cover
- delay: 1s
# move a little up again to adjust for momentum
- uart.write:
id: desk_uart
data: [0x9b, 0x06, 0x02, 0x01, 0x00, 0xfc, 0xa0, 0x9d] # up
I have the following problem: If I connect ESP D5 and controller 6, everything but keypad inputs work. If I connect controller 6 and keypad 6, everything but home assistant inputs work. But if I connect all 3, nothing works. So how can I get the best of both worlds? It looks as if the keypad cannot send any data. Nothing is logged when a button is pressed. So maybe keypad 6 is wired incorrectly?