樂歌 Loctek 升降桌 ET119 有提供 UART 通訊介面,透過 ESP 開發板與 ESPHome 可加入 Home Assistant 進行遠端與自動化控制
一、通訊架構
HCB223A-1 <–UART–> ESP開發板(ESPhome) <–WIFI–> Home Assistant
二、ESPHome
( DT-06 開發板 ESPHome 燒錄流程, 請參考另一篇分享)
standing-desk.yaml
substitutions:
device_name: standing-desk
friendly_name: 升降桌
min_height: "73"
max_height: "123"
moving_threshold: "1000"
packages:
board: !include board/doit_dt_06.yaml
device: !include base.yaml
esphome:
comment: DT-06 for Loctek ET119
on_boot:
priority: -10
then:
- button.press: button_m
external_components:
source: github://iMicknl/LoctekMotion_IoT
components: [ loctekmotion_desk_height ]
logger:
level: DEBUG
baud_rate: 0
uart:
- id: desk_uart
rx_pin: GPIO3
tx_pin: GPIO1
baud_rate: 9600
globals:
- id: move_at
type: long
initial_value: '0'
sensor:
- platform: loctekmotion_desk_height
id: "desk_height"
name: Desk Height
on_value_range:
- below: ${min_height}
then:
- switch.turn_off: switch_down
- above: ${max_height}
then:
- switch.turn_off: switch_up
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
- lambda: |-
id(move_at) = millis();
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
binary_sensor:
- platform: template
id: moving
name: "Moving"
device_class: "moving"
lambda: |-
return millis() - id(move_at) <= ${moving_threshold};
on_state:
if:
condition:
binary_sensor.is_on: moving
then:
- light.turn_on: board_blue_led
else:
- light.turn_off: board_blue_led
- switch.turn_off: switch_down
- switch.turn_off: switch_up
switch:
- 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
- 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
- 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
disabled_by_default: true
entity_category: "config"
on_turn_on:
- delay: 3000ms
- switch.turn_off: switch_alarm
button:
- platform: template
name: "Preset 1"
icon: mdi:numeric-1-box
on_press:
- 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:
- uart.write:
id: desk_uart
data: [0x9b, 0x06, 0x02, 0x08, 0x00, 0xac, 0xa6, 0x9d]
- platform: template
name: "Preset 3"
icon: mdi:numeric-3-box
on_press:
- uart.write:
id: desk_uart
data: [0x9b, 0x06, 0x02, 0x10, 0x00, 0xac, 0xac, 0x9d]
- platform: template
name: "Preset 4"
icon: mdi:numeric-4-box
on_press:
- uart.write:
id: desk_uart
data: [0x9b, 0x06, 0x02, 0x00, 0x01, 0xac, 0x60, 0x9d]
- platform: template
name: "Memory"
id: button_m
icon: mdi:alpha-m-box
entity_category: "config"
internal: true
on_press:
- uart.write:
id: desk_uart
data: [0x9b, 0x06, 0x02, 0x20, 0x00, 0xac, 0xb8, 0x9d]
- platform: template
name: "Alarm"
id: button_alarm
icon: mdi:alarm
disabled_by_default: true
on_press:
- uart.write:
id: desk_uart
data: [0x9b, 0x06, 0x02, 0x40, 0x00, 0xAC, 0x90, 0x9d]
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_m
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
doit_dt_06.yaml
# Chip is ESP8285N08
# Features: WiFi, Embedded Flash
# Crystal is 26MHz
# Detected flash size: 1MB
esp8266:
board: esp8285
light:
- platform: status_led
name: "Status LED"
id: board_blue_led
output: light_output
output:
- platform: gpio
id: light_output
pin:
number: GPIO4
inverted: True
base.yaml
esphome:
name: $device_name
friendly_name: $friendly_name
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
fast_connect: true
ap:
ssid: "$friendly_name"
password: "12345678"
captive_portal:
ota:
- platform: esphome
api:
web_server:
version: 3
port: 80
secrets.yaml
# Your Wi-Fi SSID and password
wifi_ssid: "WIFI_SSID"
wifi_password: "WIFI_PASSWORD"
三、接線
HCB223A-1 可用 RJ45 接頭,或直接焊接 RJ45 接頭背後端點
HCB223A-1 <> DT-06
(RJ45 Pin8)+5v <> VCC
(RJ45 Pin7)GND <> GND
(RJ45 Pin6)RXD <> TXD
(RJ45 Pin5)TXD <> RXD
四、 Entities Card
type: entities
entities:
- entity: sensor.standing_desk_desk_height
type: custom:multiple-entity-row
name: 升降桌
styles:
width: 45px
state_color: true
icon: mdi:desk
entities:
- icon: mdi:arrow-up-bold-box
state_color: true
styles:
width: 15px
tap_action:
action: call-service
service: cover.open_cover
service_data:
entity_id: cover.standing_desk_desk
- icon: mdi:pause-box
state_color: true
styles:
width: 15px
tap_action:
action: call-service
service: cover.stop_cover
service_data:
entity_id: cover.standing_desk_desk
- icon: mdi:arrow-down-bold-box
state_color: true
styles:
width: 15px
tap_action:
action: call-service
service: cover.close_cover
service_data:
entity_id: cover.standing_desk_desk
- icon: mdi:numeric-1-box
state_color: true
styles:
width: 15px
tap_action:
action: call-service
service: button.press
service_data:
entity_id: button.standing_desk_preset_1
- icon: mdi:numeric-2-box
state_color: true
styles:
width: 15px
tap_action:
action: call-service
service: button.press
service_data:
entity_id: button.standing_desk_preset_2
- icon: mdi:numeric-3-box
state_color: true
styles:
width: 15px
tap_action:
action: call-service
service: button.press
service_data:
entity_id: button.standing_desk_preset_3
- icon: mdi:numeric-4-box
state_color: true
styles:
width: 15px
tap_action:
action: call-service
service: button.press
service_data:
entity_id: button.standing_desk_preset_4