initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
**/__pycache__/**
|
||||||
4
.vimrc
Normal file
4
.vimrc
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
if SpmSetProject("esphome_components") == 0
|
||||||
|
nmap <leader>h yy'fpwd2f:f)lC;<esc>==mf^w
|
||||||
|
nmap <leader>s 02w"aye2b02w"byej/::a
|
||||||
|
Vf{%jd/::b
|
||||||
120
components/jiecang_desk_controller/README.md
Normal file
120
components/jiecang_desk_controller/README.md
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
# ESPHome Jiecang Desk Controller
|
||||||
|
|
||||||
|
[ESPHome](https://esphome.io/) component for controlling Jiecang desk controllers via their serial protocol.
|
||||||
|
|
||||||
|
Attention: I only have one [RJ12 model](https://www.jiecang.com/product/jcb35m11c.html) to test this
|
||||||
|
but it's likely that other Jiecang controllers are supported as their serial protocol should be compatible.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### What you need
|
||||||
|
|
||||||
|
* ESPHome compatible microcontroller
|
||||||
|
* depending on your model of desk controller
|
||||||
|
* cable with RJ12 connector (phone cable, RJ11 may also work)
|
||||||
|
* cable with RJ45 connector (network cable)
|
||||||
|
|
||||||
|
### Wiring
|
||||||
|
|
||||||
|
#### RJ12
|
||||||
|
|
||||||
|
Please double check this for your specific model!
|
||||||
|
|
||||||
|
pin | function
|
||||||
|
----|---------
|
||||||
|
1 | NC (pulled up)
|
||||||
|
2 | GND
|
||||||
|
3 | TX
|
||||||
|
4 | VCC
|
||||||
|
5 | GND
|
||||||
|
6 | NC (pulled up)
|
||||||
|
|
||||||
|
#### RJ45
|
||||||
|
|
||||||
|
Untested and only for reference!
|
||||||
|
|
||||||
|
pin | function
|
||||||
|
----|---------
|
||||||
|
1 | HS3 [^1]
|
||||||
|
2 | TX
|
||||||
|
3 | GND
|
||||||
|
4 | RX
|
||||||
|
5 | VCC
|
||||||
|
6 | HS2 [^1]
|
||||||
|
7 | HS1 [^1]
|
||||||
|
8 | HS0 [^1]
|
||||||
|
|
||||||
|
[^1]: not used here
|
||||||
|
|
||||||
|
#### microcontroller
|
||||||
|
|
||||||
|
ESP | desk
|
||||||
|
----|-----
|
||||||
|
GND | GND
|
||||||
|
5V | VCC
|
||||||
|
RX | TX
|
||||||
|
TX | RX
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
uart:
|
||||||
|
id: uart_bus
|
||||||
|
tx_pin: TX
|
||||||
|
rx_pin: RX
|
||||||
|
baud_rate: 9600
|
||||||
|
|
||||||
|
external_components:
|
||||||
|
- source:
|
||||||
|
type: git
|
||||||
|
url: https://github.com/Rocka84/esphome_components/
|
||||||
|
components: [ jiecang_desk_controller ]
|
||||||
|
|
||||||
|
jiecang_desk_controller:
|
||||||
|
id: my_desk
|
||||||
|
sensors:
|
||||||
|
height:
|
||||||
|
name: "Height"
|
||||||
|
buttons:
|
||||||
|
raise:
|
||||||
|
name: "Raise"
|
||||||
|
position1:
|
||||||
|
name: "Position 1"
|
||||||
|
|
||||||
|
button:
|
||||||
|
- platform: template
|
||||||
|
name: "Lower"
|
||||||
|
on_press:
|
||||||
|
lambda: "id(my_desk).lower();"
|
||||||
|
```
|
||||||
|
|
||||||
|
See also [example_jiecang_desk_controller.yaml](../../example_jiecang_desk_controller.yaml).
|
||||||
|
|
||||||
|
### Available sensors
|
||||||
|
|
||||||
|
sensor | description
|
||||||
|
-----------|----------------------------
|
||||||
|
height | current height of the desk
|
||||||
|
height_pct | height in percent
|
||||||
|
height_min | minimal height
|
||||||
|
height_max | maximal height
|
||||||
|
position1 | height of 1st stored height
|
||||||
|
position2 | height of 2nd stored height
|
||||||
|
position3 | height of 3rd stored height
|
||||||
|
position4 | height of 4th stored height
|
||||||
|
|
||||||
|
### Available buttons and methods
|
||||||
|
|
||||||
|
button | lambda method | description
|
||||||
|
-----------|--------------------------------|---------------------------
|
||||||
|
raise | `id(my_desk).raise()` | raise desk by one step (~14mm)
|
||||||
|
lower | `id(my_desk).raise()` | lower desk by one step (~14mm)
|
||||||
|
position1 | `id(my_desk).goto_position(1)` | move to 1st stored height
|
||||||
|
position2 | `id(my_desk).goto_position(2)` | move to 2nd stored height
|
||||||
|
position3 | `id(my_desk).goto_position(3)` | move to 3rd stored height
|
||||||
|
position4 | `id(my_desk).goto_position(4)` | move to 4th stored height
|
||||||
|
|
||||||
|
## Sources
|
||||||
|
|
||||||
|
Thanks to [phord/Jarvis](https://github.com/phord/Jarvis) for reverse engineering the UART interface and most control messages
|
||||||
|
|
||||||
128
components/jiecang_desk_controller/__init__.py
Normal file
128
components/jiecang_desk_controller/__init__.py
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
import esphome.codegen as cg
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome import pins
|
||||||
|
from esphome.components import uart, sensor, button
|
||||||
|
from esphome.const import CONF_ID, CONF_HEIGHT, UNIT_CENTIMETER, UNIT_PERCENT
|
||||||
|
|
||||||
|
DEPENDENCIES = ['uart']
|
||||||
|
AUTO_LOAD = ['sensor', 'button']
|
||||||
|
|
||||||
|
jiecang_desk_controller_ns = cg.esphome_ns.namespace('jiecang_desk_controller')
|
||||||
|
|
||||||
|
JiecangDeskController = jiecang_desk_controller_ns.class_('JiecangDeskController', cg.Component, uart.UARTDevice)
|
||||||
|
JiecangDeskButton = jiecang_desk_controller_ns.class_('JiecangDeskButton', button.Button, cg.Component)
|
||||||
|
|
||||||
|
|
||||||
|
CONF_SENSORS = "sensors"
|
||||||
|
CONF_BUTTONS = "buttons"
|
||||||
|
CONF_UNIT = "unit"
|
||||||
|
CONF_HEIGHT_MIN = "height_min"
|
||||||
|
CONF_HEIGHT_MAX = "height_max"
|
||||||
|
CONF_HEIGHT_PCT = "height_pct"
|
||||||
|
CONF_POSITION1 = "position1"
|
||||||
|
CONF_POSITION2 = "position2"
|
||||||
|
CONF_POSITION3 = "position3"
|
||||||
|
CONF_POSITION4 = "position4"
|
||||||
|
CONF_RAISE = "raise"
|
||||||
|
CONF_LOWER = "lower"
|
||||||
|
|
||||||
|
|
||||||
|
button_constants = {}
|
||||||
|
button_constants[CONF_RAISE] = 0
|
||||||
|
button_constants[CONF_LOWER] = 1
|
||||||
|
button_constants[CONF_POSITION1] = 2
|
||||||
|
button_constants[CONF_POSITION2] = 3
|
||||||
|
button_constants[CONF_POSITION3] = 4
|
||||||
|
button_constants[CONF_POSITION4] = 5
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = cv.COMPONENT_SCHEMA.extend({
|
||||||
|
cv.GenerateID(): cv.declare_id(JiecangDeskController),
|
||||||
|
cv.Optional(CONF_SENSORS): cv.Schema({
|
||||||
|
cv.Optional(CONF_HEIGHT): sensor.sensor_schema(
|
||||||
|
accuracy_decimals = 1,
|
||||||
|
unit_of_measurement = UNIT_CENTIMETER
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_UNIT): sensor.sensor_schema(
|
||||||
|
accuracy_decimals = 0
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_HEIGHT_PCT): sensor.sensor_schema(
|
||||||
|
accuracy_decimals = 1,
|
||||||
|
unit_of_measurement = UNIT_PERCENT
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_HEIGHT_MIN): sensor.sensor_schema(
|
||||||
|
accuracy_decimals = 1,
|
||||||
|
unit_of_measurement = UNIT_CENTIMETER
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_HEIGHT_MAX): sensor.sensor_schema(
|
||||||
|
accuracy_decimals = 1,
|
||||||
|
unit_of_measurement = UNIT_CENTIMETER
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_POSITION1): sensor.sensor_schema(
|
||||||
|
accuracy_decimals = 1,
|
||||||
|
unit_of_measurement = UNIT_CENTIMETER
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_POSITION2): sensor.sensor_schema(
|
||||||
|
accuracy_decimals = 1,
|
||||||
|
unit_of_measurement = UNIT_CENTIMETER
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_POSITION3): sensor.sensor_schema(
|
||||||
|
accuracy_decimals = 1,
|
||||||
|
unit_of_measurement = UNIT_CENTIMETER
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_POSITION4): sensor.sensor_schema(
|
||||||
|
accuracy_decimals = 1,
|
||||||
|
unit_of_measurement = UNIT_CENTIMETER
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
cv.Optional(CONF_BUTTONS): cv.Schema({
|
||||||
|
cv.Optional(CONF_RAISE): button.BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}),
|
||||||
|
cv.Optional(CONF_LOWER): button.BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}),
|
||||||
|
cv.Optional(CONF_POSITION1): button.BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}),
|
||||||
|
cv.Optional(CONF_POSITION2): button.BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}),
|
||||||
|
cv.Optional(CONF_POSITION3): button.BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}),
|
||||||
|
cv.Optional(CONF_POSITION4): button.BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}),
|
||||||
|
}),
|
||||||
|
}).extend(uart.UART_DEVICE_SCHEMA)
|
||||||
|
|
||||||
|
async def to_code(config):
|
||||||
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
await cg.register_component(var, config)
|
||||||
|
await uart.register_uart_device(var, config)
|
||||||
|
|
||||||
|
if CONF_SENSORS in config:
|
||||||
|
sensors = config[CONF_SENSORS]
|
||||||
|
|
||||||
|
if CONF_HEIGHT in sensors:
|
||||||
|
sens = await sensor.new_sensor(sensors[CONF_HEIGHT])
|
||||||
|
cg.add(var.set_sensor_height(sens))
|
||||||
|
if CONF_UNIT in sensors:
|
||||||
|
sens = await sensor.new_sensor(sensors[CONF_UNIT])
|
||||||
|
cg.add(var.set_sensor_unit(sens))
|
||||||
|
if CONF_HEIGHT_MIN in sensors:
|
||||||
|
sens = await sensor.new_sensor(sensors[CONF_HEIGHT_MIN])
|
||||||
|
cg.add(var.set_sensor_height_min(sens))
|
||||||
|
if CONF_HEIGHT_MAX in sensors:
|
||||||
|
sens = await sensor.new_sensor(sensors[CONF_HEIGHT_MAX])
|
||||||
|
cg.add(var.set_sensor_height_max(sens))
|
||||||
|
if CONF_HEIGHT_PCT in sensors:
|
||||||
|
sens = await sensor.new_sensor(sensors[CONF_HEIGHT_PCT])
|
||||||
|
cg.add(var.set_sensor_height_pct(sens))
|
||||||
|
if CONF_POSITION1 in sensors:
|
||||||
|
sens = await sensor.new_sensor(sensors[CONF_POSITION1])
|
||||||
|
cg.add(var.set_sensor_position1(sens))
|
||||||
|
if CONF_POSITION2 in sensors:
|
||||||
|
sens = await sensor.new_sensor(sensors[CONF_POSITION2])
|
||||||
|
cg.add(var.set_sensor_position2(sens))
|
||||||
|
if CONF_POSITION3 in sensors:
|
||||||
|
sens = await sensor.new_sensor(sensors[CONF_POSITION3])
|
||||||
|
cg.add(var.set_sensor_position3(sens))
|
||||||
|
if CONF_POSITION4 in sensors:
|
||||||
|
sens = await sensor.new_sensor(sensors[CONF_POSITION4])
|
||||||
|
cg.add(var.set_sensor_position4(sens))
|
||||||
|
|
||||||
|
if CONF_BUTTONS in config:
|
||||||
|
buttons = config[CONF_BUTTONS]
|
||||||
|
for button_type in buttons.keys():
|
||||||
|
btn = await button.new_button(buttons[button_type])
|
||||||
|
cg.add(var.add_button(btn, button_constants[button_type]))
|
||||||
|
|
||||||
203
components/jiecang_desk_controller/jiecang_desk_controller.cpp
Normal file
203
components/jiecang_desk_controller/jiecang_desk_controller.cpp
Normal file
@@ -0,0 +1,203 @@
|
|||||||
|
#include "jiecang_desk_controller.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace jiecang_desk_controller {
|
||||||
|
|
||||||
|
static const char *const TAG = "jiecang_desk_controller";
|
||||||
|
|
||||||
|
float JiecangDeskController::byte2float(int high, int low) {
|
||||||
|
return static_cast<float>((high<<8) + low)/10;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool JiecangDeskController::bufferMessage(int data, unsigned int *buffer, int len) {
|
||||||
|
// This is a very simple method of receiving messages from the desk.
|
||||||
|
// It will fail on messages that contain the value 0x7E in their payload.
|
||||||
|
// But it is super simple and works for the messages we care about.
|
||||||
|
|
||||||
|
// format: 0xF2 0xF2 [command] [param_count] [[param] ...] [checksum] 0x7E
|
||||||
|
// checksum: sum of [command], [param_count] and all [param]s
|
||||||
|
|
||||||
|
static int cmd_incoming = 0; // 0: wait for F2, 1: wait for 2nd F2, 2: buffer data
|
||||||
|
static int pos = 0;
|
||||||
|
|
||||||
|
if (cmd_incoming < 2 && data == 0xF2) { // start of message, must appear twice
|
||||||
|
cmd_incoming++;
|
||||||
|
pos = 0;
|
||||||
|
|
||||||
|
} else if (cmd_incoming == 1) { // no second F2 received
|
||||||
|
cmd_incoming = 0;
|
||||||
|
|
||||||
|
} else if (cmd_incoming == 2) {
|
||||||
|
if (data == 0x7E) { // end of message
|
||||||
|
cmd_incoming = 0;
|
||||||
|
for (;pos<len-1;pos++) { // fill rest of buffer with zeros
|
||||||
|
buffer[pos]=0;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
|
||||||
|
} else if (pos >= len) { // message too long, drop it
|
||||||
|
cmd_incoming = 0;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
buffer[pos++] = data; // buffer data
|
||||||
|
}
|
||||||
|
|
||||||
|
} // else: received garbage
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JiecangDeskController::handleMessage(unsigned int *message) {
|
||||||
|
// ESP_LOGV("jiecang_desk_controller", "message %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", message[0], message[1], message[2], message[3], message[4], message[5], message[6], message[7], message[8], message[9]);
|
||||||
|
|
||||||
|
switch (message[0]) {
|
||||||
|
case 0x01:
|
||||||
|
ESP_LOGV("jiecang_desk_controller", "height 0x%0X%0X", message[2], message[3]);
|
||||||
|
if (height != nullptr) height->publish_state(byte2float(message[2], message[3]));
|
||||||
|
|
||||||
|
if (height_pct != nullptr)
|
||||||
|
height_pct->publish_state((height->state - height_min->state) / (height_max->state - height_min->state) * 100);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x0e:
|
||||||
|
ESP_LOGV("jiecang_desk_controller", "unit 0x%0X", message[2]);
|
||||||
|
if (unit != nullptr) unit->publish_state(message[2]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x20:
|
||||||
|
ESP_LOGV("jiecang_desk_controller", "limits 0x%0X max %i min %i", message[2], (message[2] & 1), (message[2]>>4));
|
||||||
|
|
||||||
|
if (height_min != nullptr && (message[2] & 1) == 0) { // low nibble 0 -> no max limit, use physical_max
|
||||||
|
height_max->publish_state(physical_max);
|
||||||
|
}
|
||||||
|
if (height_max != nullptr && (message[2]>>4) == 0) { // high nibble 0 -> no min limit, use physical_min
|
||||||
|
height_min->publish_state(physical_min);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x07:
|
||||||
|
ESP_LOGV("jiecang_desk_controller", "physical limits 0x%02X%02X 0x%02X%02X", message[2], message[3], message[4], message[5]);
|
||||||
|
physical_max = byte2float(message[2], message[3]);
|
||||||
|
physical_min = byte2float(message[4], message[5]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x21:
|
||||||
|
ESP_LOGV("jiecang_desk_controller", "height_max 0x%02X%02X", message[2], message[3]);
|
||||||
|
if (height_max != nullptr) height_max->publish_state(byte2float(message[2], message[3]));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x22:
|
||||||
|
ESP_LOGV("jiecang_desk_controller", "height_min 0x%02X%02X", message[2], message[3]);
|
||||||
|
if (height_min != nullptr) height_min->publish_state(byte2float(message[2], message[3]));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x25:
|
||||||
|
ESP_LOGV("jiecang_desk_controller", "position1 0x%02X%02X", message[2], message[3]);
|
||||||
|
if (position1 != nullptr) position1->publish_state(byte2float(message[2], message[3]));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x26:
|
||||||
|
ESP_LOGV("jiecang_desk_controller", "position2 0x%02X%02X", message[2], message[3]);
|
||||||
|
if (position2 != nullptr) position2->publish_state(byte2float(message[2], message[3]));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x27:
|
||||||
|
ESP_LOGV("jiecang_desk_controller", "position3 0x%02X%02X", message[2], message[3]);
|
||||||
|
if (position3 != nullptr) position3->publish_state(byte2float(message[2], message[3]));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x28:
|
||||||
|
ESP_LOGV("jiecang_desk_controller", "position4 0x%02X%02X", message[2], message[3]);
|
||||||
|
if (position4 != nullptr) position4->publish_state(byte2float(message[2], message[3]));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ESP_LOGV("jiecang_desk_controller", "unknown message %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", message[0], message[1], message[2], message[3], message[4], message[5], message[6], message[7], message[8], message[9]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JiecangDeskController::update() {
|
||||||
|
const int max_length = 10;
|
||||||
|
static unsigned int buffer[max_length];
|
||||||
|
while (available()) {
|
||||||
|
if(bufferMessage(read(), buffer, max_length)) {
|
||||||
|
handleMessage(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JiecangDeskController::send_simple_command(unsigned char cmd) {
|
||||||
|
write_array({ 0xF1, 0xF1, cmd, 0x00, cmd, 0x7E });
|
||||||
|
}
|
||||||
|
|
||||||
|
void JiecangDeskController::add_button(button::Button *btn, int action) {
|
||||||
|
btn->add_on_press_callback([this, action]() { this->button_press_action(action); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void JiecangDeskController::raise() {
|
||||||
|
send_simple_command(0x01);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JiecangDeskController::lower() {
|
||||||
|
send_simple_command(0x02);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JiecangDeskController::goto_position(int pos) {
|
||||||
|
switch (pos) {
|
||||||
|
case 1:
|
||||||
|
send_simple_command(0x05);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
send_simple_command(0x06);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
send_simple_command(0x27);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
send_simple_command(0x28);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JiecangDeskController::request_physical_limits() {
|
||||||
|
send_simple_command(0x0C);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JiecangDeskController::request_limits() {
|
||||||
|
send_simple_command(0x20);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JiecangDeskController::request_settings() {
|
||||||
|
send_simple_command(0x07);
|
||||||
|
}
|
||||||
|
|
||||||
|
void JiecangDeskController::button_press_action(int action) {
|
||||||
|
ESP_LOGV("JiecangDeskController", "button_press_action %i", action);
|
||||||
|
switch (action) {
|
||||||
|
case BUTTON_RAISE:
|
||||||
|
raise();
|
||||||
|
break;
|
||||||
|
case BUTTON_LOWER:
|
||||||
|
lower();
|
||||||
|
break;
|
||||||
|
case BUTTON_POSITION1:
|
||||||
|
goto_position(1);
|
||||||
|
break;
|
||||||
|
case BUTTON_POSITION2:
|
||||||
|
goto_position(2);
|
||||||
|
break;
|
||||||
|
case BUTTON_POSITION3:
|
||||||
|
goto_position(3);
|
||||||
|
break;
|
||||||
|
case BUTTON_POSITION4:
|
||||||
|
goto_position(4);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void JiecangDeskButton::press_action() {}
|
||||||
|
|
||||||
|
} //namespace jiecang_desk_controller
|
||||||
|
} //namespace esphome
|
||||||
|
|
||||||
76
components/jiecang_desk_controller/jiecang_desk_controller.h
Normal file
76
components/jiecang_desk_controller/jiecang_desk_controller.h
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <future>
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
#include "esphome/components/button/button.h"
|
||||||
|
#include "esphome/components/uart/uart.h"
|
||||||
|
#include "esphome/core/hal.h"
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
|
||||||
|
#define BUTTON_RAISE 0
|
||||||
|
#define BUTTON_LOWER 1
|
||||||
|
#define BUTTON_POSITION1 2
|
||||||
|
#define BUTTON_POSITION2 3
|
||||||
|
#define BUTTON_POSITION3 4
|
||||||
|
#define BUTTON_POSITION4 5
|
||||||
|
|
||||||
|
namespace esphome {
|
||||||
|
namespace jiecang_desk_controller {
|
||||||
|
|
||||||
|
class JiecangDeskController : public PollingComponent, public sensor::Sensor, public uart::UARTDevice {
|
||||||
|
private:
|
||||||
|
float physical_min = 0;
|
||||||
|
float physical_max = 0;
|
||||||
|
|
||||||
|
float byte2float(int high, int low);
|
||||||
|
bool bufferMessage(int data, unsigned int *buffer, int len);
|
||||||
|
void handleMessage(unsigned int *message);
|
||||||
|
|
||||||
|
public:
|
||||||
|
void update() override;
|
||||||
|
|
||||||
|
void set_sensor_height(sensor::Sensor *sensor) { this->height = sensor; }
|
||||||
|
void set_sensor_unit(sensor::Sensor *sensor) { this->unit = sensor; }
|
||||||
|
void set_sensor_height_min(sensor::Sensor *sensor) { this->height_min = sensor; }
|
||||||
|
void set_sensor_height_max(sensor::Sensor *sensor) { this->height_max = sensor; }
|
||||||
|
void set_sensor_height_pct(sensor::Sensor *sensor) { this->height_pct = sensor; }
|
||||||
|
|
||||||
|
void set_sensor_position1(sensor::Sensor *sensor) { this->position1 = sensor; }
|
||||||
|
void set_sensor_position2(sensor::Sensor *sensor) { this->position2 = sensor; }
|
||||||
|
void set_sensor_position3(sensor::Sensor *sensor) { this->position3 = sensor; }
|
||||||
|
void set_sensor_position4(sensor::Sensor *sensor) { this->position4 = sensor; }
|
||||||
|
|
||||||
|
void send_simple_command(unsigned char cmd);
|
||||||
|
void add_button(button::Button *btn, int action);
|
||||||
|
|
||||||
|
void raise();
|
||||||
|
void lower();
|
||||||
|
void goto_position(int pos);
|
||||||
|
|
||||||
|
void request_physical_limits();
|
||||||
|
void request_limits();
|
||||||
|
void request_settings();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Sensor *height{nullptr};
|
||||||
|
Sensor *unit{nullptr};
|
||||||
|
Sensor *height_min{nullptr};
|
||||||
|
Sensor *height_max{nullptr};
|
||||||
|
Sensor *height_pct{nullptr};
|
||||||
|
|
||||||
|
Sensor *position1{nullptr};
|
||||||
|
Sensor *position2{nullptr};
|
||||||
|
Sensor *position3{nullptr};
|
||||||
|
Sensor *position4{nullptr};
|
||||||
|
|
||||||
|
void button_press_action(int type);
|
||||||
|
};
|
||||||
|
|
||||||
|
class JiecangDeskButton : public Component, public button::Button {
|
||||||
|
protected:
|
||||||
|
void press_action() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} //namespace jiecang_desk_controller
|
||||||
|
} //namespace esphome
|
||||||
108
example_jiecang_desk_controller.yaml
Normal file
108
example_jiecang_desk_controller.yaml
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
esphome:
|
||||||
|
name: jiecang-desk-controller
|
||||||
|
friendly_name: Jiecang Desk Controller
|
||||||
|
on_boot:
|
||||||
|
priority: 0 # when mostly everything else is done
|
||||||
|
then:
|
||||||
|
- lambda: "id(my_desk).request_physical_limits();"
|
||||||
|
- delay: 0.1s
|
||||||
|
- lambda: "id(my_desk).request_limits();"
|
||||||
|
- delay: 0.1s
|
||||||
|
- lambda: "id(my_desk).request_settings();"
|
||||||
|
|
||||||
|
esp8266:
|
||||||
|
board: esp01_1m
|
||||||
|
|
||||||
|
logger:
|
||||||
|
level: WARN
|
||||||
|
baud_rate: 0 # disable logging over uart, required when using the RX/TX pins for the controller
|
||||||
|
|
||||||
|
api:
|
||||||
|
|
||||||
|
ota:
|
||||||
|
|
||||||
|
wifi:
|
||||||
|
ssid: !secret wifi_ssid
|
||||||
|
password: !secret wifi_password
|
||||||
|
|
||||||
|
# Enable fallback hotspot (captive portal) in case wifi connection fails
|
||||||
|
ap:
|
||||||
|
ssid: "esphome-desk"
|
||||||
|
password: "9ebc6eac0b4e0e26b8d3b955ec660557"
|
||||||
|
|
||||||
|
captive_portal:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uart:
|
||||||
|
id: uart_bus
|
||||||
|
tx_pin: TX
|
||||||
|
rx_pin: RX
|
||||||
|
baud_rate: 9600
|
||||||
|
|
||||||
|
external_components:
|
||||||
|
- source:
|
||||||
|
type: git
|
||||||
|
url: https://github.com/Rocka84/esphome_components/
|
||||||
|
components: [ jiecang_desk_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:
|
||||||
|
raise:
|
||||||
|
name: "Raise"
|
||||||
|
lower:
|
||||||
|
name: "Lower"
|
||||||
|
position1:
|
||||||
|
name: "Position 1"
|
||||||
|
position2:
|
||||||
|
name: "Position 2"
|
||||||
|
position3:
|
||||||
|
name: "Position 3"
|
||||||
|
position4:
|
||||||
|
name: "Position 4"
|
||||||
|
|
||||||
|
## lambda usage
|
||||||
|
# button:
|
||||||
|
# - platform: template
|
||||||
|
# name: "Raise"
|
||||||
|
# on_press:
|
||||||
|
# lambda: "id(my_desk).raise();"
|
||||||
|
# - platform: template
|
||||||
|
# name: "Lower"
|
||||||
|
# on_press:
|
||||||
|
# lambda: "id(my_desk).lower();"
|
||||||
|
# - platform: template
|
||||||
|
# name: "Position 1"
|
||||||
|
# on_press:
|
||||||
|
# lambda: "id(my_desk).goto_position(1);"
|
||||||
|
# - platform: template
|
||||||
|
# name: "Position 2"
|
||||||
|
# on_press:
|
||||||
|
# lambda: "id(my_desk).goto_position(2);"
|
||||||
|
# - platform: template
|
||||||
|
# name: "Position 3"
|
||||||
|
# on_press:
|
||||||
|
# lambda: "id(my_desk).goto_position(3);"
|
||||||
|
# - platform: template
|
||||||
|
# name: "Position 4"
|
||||||
|
# on_press:
|
||||||
|
# lambda: "id(my_desk).goto_position(4);"
|
||||||
|
|
||||||
Reference in New Issue
Block a user