Maidesite Desk Refactoring

This commit is contained in:
ElVit 2025-07-12 10:47:37 +02:00
parent 9b1d64bb09
commit 92c764a105
12 changed files with 477 additions and 368 deletions

View File

@ -11,6 +11,7 @@ DEPENDENCIES = ['uart']
CONF_MAIDESITE_DESK_ID = "maidesite_desk"
CONF_LOG_UART_MSG = "log_uart_msg"
maidesite_desk_ns = cg.esphome_ns.namespace('maidesite_desk')
MaidesiteDeskComponent = maidesite_desk_ns.class_('MaidesiteDeskComponent', cg.Component, uart.UARTDevice)
@ -22,12 +23,13 @@ CONFIG_SCHEMA = (
cv.Optional(CONF_LOG_UART_MSG, default=False): cv.boolean,
}
)
.extend(uart.UART_DEVICE_SCHEMA)
.extend(cv.COMPONENT_SCHEMA)
.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)
cg.add(var.set_log_uart_msg(config[CONF_LOG_UART_MSG]))

View File

@ -41,7 +41,9 @@ MaidesiteDeskButton = maidesite_desk_ns.class_("MaidesiteDeskButton", button.But
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_MAIDESITE_DESK_ID): cv.use_id(MaidesiteDeskComponent),
cv.GenerateID(CONF_MAIDESITE_DESK_ID): cv.use_id(
MaidesiteDeskComponent
),
cv.Optional(CONF_STEP_UP): button.button_schema(
MaidesiteDeskButton,
@ -90,11 +92,11 @@ CONFIG_SCHEMA = cv.Schema(
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):
hub = await cg.get_variable(config[CONF_MAIDESITE_DESK_ID])
parent = await cg.get_variable(config[CONF_MAIDESITE_DESK_ID])
for index, key in enumerate(TYPES):
if child_config := config.get(key):
var = await button.new_button(child_config)
await cg.register_component(var, child_config)
await cg.register_parented(var, config[CONF_MAIDESITE_DESK_ID])
cg.add(getattr(hub, f"set_{key}_button")(var))
cg.add(var.set_parent(parent))
cg.add(var.set_id(index))
cg.add(parent.add_number(var))

View File

@ -16,7 +16,87 @@ namespace esphome
void MaidesiteDeskButton::press_action()
{
this->parent_->button_press_action(this);
switch (this->id_)
{
case ButtonIds::CONF_STEP_UP:
{
this->parent_->send_1byte_command(0x01);
break;
}
case ButtonIds::CONF_STEP_DOWN:
{
this->parent_->send_1byte_command(0x02);
break;
}
case ButtonIds::CONF_STOP:
{
this->parent_->send_1byte_command(0x2B);
break;
}
case ButtonIds::CONF_GOTO_MAX:
{
this->parent_->goto_max_position();
break;
}
case ButtonIds::CONF_GOTO_MIN:
{
this->parent_->goto_min_position();
break;
}
case ButtonIds::CONF_GOTO_M1:
{
this->parent_->send_1byte_command(0x05);
break;
}
case ButtonIds::CONF_GOTO_M2:
{
this->parent_->send_1byte_command(0x06);
break;
}
case ButtonIds::CONF_GOTO_M3:
{
this->parent_->send_1byte_command(0x27);
break;
}
case ButtonIds::CONF_GOTO_M4:
{
this->parent_->send_1byte_command(0x28);
break;
}
case ButtonIds::CONF_SAVE_M1:
{
// set memory position
this->parent_->send_1byte_command(0x03);
// request settings
this->parent_->send_1byte_command(0x07);
break;
}
case ButtonIds::CONF_SAVE_M2:
{
// set memory position
this->parent_->send_1byte_command(0x04);
// request settings
this->parent_->send_1byte_command(0x07);
break;
}
case ButtonIds::CONF_SAVE_M3:
{
// set memory position
this->parent_->send_1byte_command(0x25);
// request settings
this->parent_->send_1byte_command(0x07);
break;
}
case ButtonIds::CONF_SAVE_M4:
{
// set memory position
this->parent_->send_1byte_command(0x26);
// request settings
this->parent_->send_1byte_command(0x07);
break;
}
default: return;
};
}
} // namespace maidesite_desk
} // namespace esphome

View File

@ -8,16 +8,33 @@ namespace esphome
{
namespace maidesite_desk
{
class MaidesiteDeskButton : public button::Button, public Component, public Parented<MaidesiteDeskComponent>
enum ButtonIds : uint8_t
{
CONF_STEP_UP,
CONF_STEP_DOWN,
CONF_STOP,
CONF_GOTO_MAX,
CONF_GOTO_MIN,
CONF_GOTO_M1,
CONF_GOTO_M2,
CONF_GOTO_M3,
CONF_GOTO_M4,
CONF_SAVE_M1,
CONF_SAVE_M2,
CONF_SAVE_M3,
CONF_SAVE_M4,
};
class MaidesiteDeskButton : public button::Button, public Component,
public Parented<MaidesiteDeskComponent>, public MaidesiteDeskEntity
{
public:
MaidesiteDeskButton() = default;
void dump_config() override;
void set_id(int id) { this->id_ = id; }
void publish_new_state(const std::vector<uint8_t>& data) override {}
protected:
void press_action() override;
int id_;
};
} // namespace maidesite_desk
} // namespace esphome

View File

@ -10,13 +10,13 @@ namespace esphome
void MaidesiteDeskComponent::dump_config()
{
ESP_LOGCONFIG(TAG, "Maidesite Desk");
delay(10);
delay(10); // NOLINT
}
void MaidesiteDeskComponent::setup()
{
ESP_LOGCONFIG(TAG, "Setting up Maidesite Desk ...");
delay(10);
delay(10); // NOLINT
this->check_uart_settings(9600);
this->request_physical_limits();
this->request_limits();
@ -25,54 +25,77 @@ namespace esphome
void MaidesiteDeskComponent::loop()
{
// 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.
this->read_response();
this->decode_response(this->desk_message_);
}
void MaidesiteDeskComponent::read_response()
{
// Read message:
// format: 0xF2 0xF2 [command] [param_count] [[param] ...] [checksum] 0x7E
// checksum: sum of [command], [param_count] and all [param]s
uint8_t oneByte;
while (this->available())
{
this->read_byte(&oneByte);
this->read_byte(&byte_);
// Message shall start with 0xF2, if not skip this byte
if (!this->response_receiving_)
{
if (oneByte != 0xF2)
if (byte_ != 0xF2)
continue;
this->response_message_.clear();
this->response_receiving_ = true;
this->checksum_ = 0;
this->checksum_passed_ = false;
}
// Add current byte to message buffer
this->response_message_.push_back(oneByte);
// Discard message if 2. byte is not 0xF2
if (this->response_message_.size() == 2 && oneByte != 0xF2)
// Add current byte to message buffer
this->response_message_.push_back(byte_);
// Discard message if 1.and 2. byte are not equal
if (this->response_message_.size() == 2 && this->response_message_[1] != byte_)
{
ESP_LOGW(TAG, "Invalid response message: 2. byte is 0x%02X but expexted is 0xF2", oneByte);
delay(10);
ESP_LOGW(TAG, "Invalid response message: 2. byte is 0x%02X but expexted is 0x%02X", byte_, this->response_message_[1]);
delay(10); // NOLINT
this->response_message_.clear();
this->response_receiving_ = false;
continue;
}
// Verify chechsum
if (this->response_message_.size() > 3 && this->response_message_.size() == this->response_message_[3] + 5)
{
this->checksum_passed_ = true;
if (byte_ != checksum_)
{
ESP_LOGW(TAG, "Calculated checksum (0x%X) is not equal to received checksum (0x%X)", checksum_, byte_);
delay(10); // NOLINT
}
}
// Calculate chechsum
if (this->response_message_.size() > 2 && !this->checksum_passed_)
{
checksum_ += byte_;
}
// Discard message if it is too long
if (this->response_message_.size() > 10)
{
ESP_LOGW(TAG, "Response message too long: expected not more than 10 bytes");
delay(10);
delay(10); // NOLINT
this->response_message_.clear();
this->response_receiving_ = false;
continue;
}
// Decode message if message is complete (last byte shall be 0x7E)
if (oneByte == 0x7E)
// Message is complete if last byte is 0x7E
if (byte_ == 0x7E && this->checksum_passed_)
{
this->log_uart_hex("<<<", this->response_message_, ',');
this->decode_response(this->response_message_);
this->response_message_.clear();
this->desk_message_ = this->response_message_;
this->response_receiving_ = false;
this->log_uart_hex("<<<", this->response_message_, ',');
}
}
}
@ -93,141 +116,46 @@ namespace esphome
for (size_t i = 0; i < logStr.length(); i += UART_LOG_CHUNK_SIZE)
{
ESP_LOGI(TAG, "%s %s", prefix.c_str(), logStr.substr(i, UART_LOG_CHUNK_SIZE).c_str());
delay(10);
delay(10); // NOLINT
}
}
void MaidesiteDeskComponent::decode_response(std::vector<uint8_t> message)
{
// ToDo: Add checksum check
switch (message[2])
if (message.empty()) return;
if (message.size() < 6)
{
case 0x01:
ESP_LOGI(TAG, "Set height to 0x%0X%0X", message[4], message[5]);
delay(10);
ESP_LOGW(TAG, "Received message too short.");
return;
}
float new_height;
new_height = byte2float(message[4], message[5]);
if (new_height == this->current_height_)
return;
this->current_height_ = new_height;
if (this->height_abs_sensor_ != nullptr)
this->height_abs_sensor_->publish_state(this->current_height_);
if (this->height_abs_number_ != nullptr)
this->height_abs_number_->publish_state(this->current_height_);
if (this->height_pct_sensor_ != nullptr && limit_max_ != 0)
this->height_pct_sensor_->publish_state((this->current_height_ - this->limit_min_) / (this->limit_max_ - this->limit_min_) * 100);
if (this->height_pct_number_ != nullptr && limit_max_ != 0)
this->height_pct_number_->publish_state(roundf((this->current_height_ - this->limit_min_) / (this->limit_max_ - this->limit_min_) * 1000) / 10);
break;
case 0x20:
ESP_LOGI(TAG, "Set limits to 0x%0X max=%i min=%i", message[4], (message[5] & 1), (message[4] >> 4));
delay(10);
if ((message[4] & 1) == 0)
{ // low nibble 0 -> no max limit, use physical_max_
this->limit_max_ = this->physical_max_;
if (this->height_max_sensor_ != nullptr)
this->height_max_sensor_->publish_state(this->limit_max_);
if (height_abs_number_ != nullptr)
this->height_abs_number_->traits.set_max_value(this->limit_max_);
}
if ((message[4] >> 4) == 0)
{ // high nibble 0 -> no min limit, use physical_min_
this->limit_min_ = this->physical_min_;
if (this->height_min_sensor_ != nullptr)
this->height_min_sensor_->publish_state(limit_min_);
if (height_abs_number_ != nullptr)
this->height_abs_number_->traits.set_min_value(limit_min_);
}
break;
case 0x07:
ESP_LOGI(TAG, "Set physical limits to min=0x%02X%02X max=0x%02X%02X", message[4], message[5], message[6], message[7]);
delay(10);
this->physical_max_ = this->byte2float(message[4], message[5]);
this->physical_min_ = this->byte2float(message[6], message[7]);
break;
case 0x21:
ESP_LOGI(TAG, "Set max. height to 0x%02X%02X", message[4], message[5]);
delay(10);
this->limit_max_ = this->byte2float(message[4], message[5]);
if (this->height_max_sensor_ != nullptr)
this->height_max_sensor_->publish_state(limit_max_);
if (height_abs_number_ != nullptr)
this->height_abs_number_->traits.set_max_value(limit_max_);
break;
case 0x22:
ESP_LOGI(TAG, "Set min. height to 0x%02X%02X", message[4], message[5]);
delay(10);
this->limit_min_ = this->byte2float(message[4], message[5]);
if (this->height_min_sensor_ != nullptr)
this->height_min_sensor_->publish_state(limit_min_);
if (height_abs_number_ != nullptr)
this->height_abs_number_->traits.set_min_value(limit_min_);
break;
case 0x25:
ESP_LOGI(TAG, "Set position m1 to 0x%02X%02X", message[4], message[5]);
delay(10);
if (this->position_m1_sensor_ != nullptr)
this->position_m1_sensor_->publish_state(this->byte2float(message[4], message[5]));
break;
case 0x26:
ESP_LOGI(TAG, "Set position m2 to 0x%02X%02X", message[4], message[5]);
delay(10);
if (this->position_m2_sensor_ != nullptr)
this->position_m2_sensor_->publish_state(this->byte2float(message[4], message[5]));
break;
case 0x27:
ESP_LOGI(TAG, "Set position m3 to 0x%02X%02X", message[4], message[5]);
delay(10);
if (this->position_m3_sensor_ != nullptr)
this->position_m3_sensor_->publish_state(this->byte2float(message[4], message[5]));
break;
case 0x28:
ESP_LOGI(TAG, "Set position m4 to 0x%02X%02X", message[4], message[5]);
delay(10);
if (this->position_m4_sensor_ != nullptr)
this->position_m4_sensor_->publish_state(this->byte2float(message[4], message[5]));
break;
for (auto *entity : this->sensors_)
{
entity->publish_new_state(message);
}
for (auto *entity : this->numbers_)
{
entity->publish_new_state(message);
}
// switch (message[2])
// {
// case 0x0E:
// ESP_LOGI(TAG, "units 0x%02X", message[4]);
// if (units != nullptr)
// units->publish_state(byte2float(message[4], message[5]));
// break;
default:
ESP_LOGI(TAG, "Received unknown message");
delay(10);
}
}
float MaidesiteDeskComponent::byte2float(int high, int low)
{
return static_cast<float>((high << 8) + low) / 10;
// default:
// ESP_LOGI(TAG, "Received unknown message");
// delay(10); // NOLINT
// }
}
// Write message:
// format: 0xF1 0xF1 [command] [param_count] [[param] ...] [checksum] 0x7E
// checksum: sum of [command], [param_count] and all [param]s
void MaidesiteDeskComponent::send_simple_command(unsigned char cmd)
void MaidesiteDeskComponent::send_1byte_command(unsigned char cmd)
{
this->request_message_.clear();
this->request_message_.insert(this->request_message_.end(), { 0xF1, 0xF1, cmd, 0x00, cmd, 0x7E });
@ -238,7 +166,7 @@ namespace esphome
void MaidesiteDeskComponent::send_2byte_command(unsigned char cmd, unsigned char high_byte, unsigned char low_byte)
{
// ToDo: make one function of send_simple_command and send_2byte_command
// ToDo: make one function of send_1byte_command and send_2byte_command
unsigned char checksum = cmd + 2 + high_byte + low_byte;
this->request_message_.clear();
@ -248,150 +176,50 @@ namespace esphome
delay(100); // NOLINT
}
void MaidesiteDeskComponent::step_up()
{
this->send_simple_command(0x01);
}
void MaidesiteDeskComponent::step_down()
{
this->send_simple_command(0x02);
}
void MaidesiteDeskComponent::stop()
{
this->send_simple_command(0x2B);
}
void MaidesiteDeskComponent::goto_mem_position(int pos)
{
switch (pos)
{
case 1:
this->send_simple_command(0x05);
break;
case 2:
this->send_simple_command(0x06);
break;
case 3:
this->send_simple_command(0x27);
break;
case 4:
this->send_simple_command(0x28);
break;
}
}
void MaidesiteDeskComponent::save_mem_position(int pos)
{
switch (pos)
{
case 1:
this->send_simple_command(0x03);
break;
case 2:
this->send_simple_command(0x04);
break;
case 3:
this->send_simple_command(0x25);
break;
case 4:
this->send_simple_command(0x26);
break;
}
}
void MaidesiteDeskComponent::request_physical_limits()
{
this->send_simple_command(0x0C);
this->send_1byte_command(0x0C);
}
void MaidesiteDeskComponent::request_limits()
{
this->send_simple_command(0x20);
this->send_1byte_command(0x20);
}
void MaidesiteDeskComponent::request_settings()
{
this->send_simple_command(0x07);
this->send_1byte_command(0x07);
}
void MaidesiteDeskComponent::request_move_to()
{
this->send_simple_command(0x1B);
this->send_1byte_command(0x1B);
}
void MaidesiteDeskComponent::goto_max_position()
{
this->goto_height(limit_max_);
this->goto_height_abs(limit_max_);
}
void MaidesiteDeskComponent::goto_min_position()
{
this->goto_height(limit_min_);
this->goto_height_abs(limit_min_);
}
void MaidesiteDeskComponent::goto_height(float height)
void MaidesiteDeskComponent::goto_height_abs(float height)
{
unsigned char high_byte = ((int)height * 10) >> 8;
unsigned char low_byte = ((int)height * 10) & 0xFF;
this->send_2byte_command(0x80, high_byte, low_byte);
this->request_move_to();
this->send_1byte_command(0x1B);
}
#ifdef USE_BUTTON
void MaidesiteDeskComponent::button_press_action(button::Button* object)
void MaidesiteDeskComponent::goto_height_pct(float height)
{
if (object == step_up_button_)
this->step_up();
else if (object == step_down_button_)
this->step_down();
else if (object == stop_button_)
this->stop();
else if (object == goto_max_button_)
this->goto_max_position();
else if (object == goto_min_button_)
this->goto_min_position();
else if (object == goto_m1_button_)
this->goto_mem_position(1);
else if (object == goto_m2_button_)
this->goto_mem_position(2);
else if (object == goto_m3_button_)
this->goto_mem_position(3);
else if (object == goto_m4_button_)
this->goto_mem_position(4);
else if (object == save_m1_button_)
{
this->save_mem_position(1);
this->request_settings();
}
else if (object == save_m2_button_)
{
this->save_mem_position(2);
this->request_settings();
}
else if (object == save_m3_button_)
{
this->save_mem_position(3);
this->request_settings();
}
else if (object == save_m4_button_)
{
this->save_mem_position(4);
this->request_settings();
}
if (limit_max_ <= 0 || limit_max_ <= limit_min_) return;
float height_abs = (limit_max_ - limit_min_) * height / 100 + limit_min_;
this->goto_height_abs(height_abs);
}
#endif
#ifdef USE_NUMBER
void MaidesiteDeskComponent::number_control(number::Number* object, float value)
{
if (object == height_abs_number_)
this->goto_height(value);
else if (object == height_pct_number_)
if (limit_max_ > 0 && limit_max_ > limit_min_)
this->goto_height((limit_max_ - limit_min_) * value / 100 + limit_min_);
}
#endif
} // namespace maidesite_desk
} // namespace esphome

View File

@ -1,19 +1,10 @@
#pragma once
#include <vector>
#include <string>
#include "esphome/core/component.h"
#include "esphome/core/defines.h"
#include "esphome/core/log.h"
#include "esphome/components/uart/uart.h"
#ifdef USE_SENSOR
#include "esphome/components/sensor/sensor.h"
#endif
#ifdef USE_BUTTON
#include "esphome/components/button/button.h"
#endif
#ifdef USE_NUMBER
#include "esphome/components/number/number.h"
#endif
#include <vector>
#include <string>
#define UART_LOG_CHUNK_SIZE 153
@ -22,93 +13,80 @@ namespace esphome
{
namespace maidesite_desk
{
class MaidesiteDeskEntity
{
public:
virtual void set_id(const int id) { id_ = id; }
virtual void publish_new_state(const std::vector<uint8_t>& data) = 0;
protected:
int id_ { -1 };
float byte2float(int high, int low) { return static_cast<float>((high << 8) + low) / 10; }
};
class MaidesiteDeskComponent : public Component, public uart::UARTDevice
{
public:
#ifdef USE_SENSOR
SUB_SENSOR(unit);
SUB_SENSOR(height_abs);
SUB_SENSOR(height_pct);
SUB_SENSOR(height_min);
SUB_SENSOR(height_max);
SUB_SENSOR(position_m1);
SUB_SENSOR(position_m2);
SUB_SENSOR(position_m3);
SUB_SENSOR(position_m4);
#endif
#ifdef USE_BUTTON
SUB_BUTTON(unit);
SUB_BUTTON(step_up);
SUB_BUTTON(step_down);
SUB_BUTTON(stop);
SUB_BUTTON(goto_max);
SUB_BUTTON(goto_min);
SUB_BUTTON(goto_m1);
SUB_BUTTON(goto_m2);
SUB_BUTTON(goto_m3);
SUB_BUTTON(goto_m4);
SUB_BUTTON(save_m1);
SUB_BUTTON(save_m2);
SUB_BUTTON(save_m3);
SUB_BUTTON(save_m4);
void button_press_action(button::Button* object);
#endif
#ifdef USE_NUMBER
SUB_NUMBER(height_abs);
SUB_NUMBER(height_pct);
void number_control(number::Number* object, float value);
#endif
MaidesiteDeskComponent() = default;
// base class functions
float get_setup_priority() const override { return setup_priority::LATE; }
float get_setup_priority() const override { return setup_priority::DATA; }
void dump_config() override;
void setup() override;
void loop() override;
// option functions
void set_log_uart_msg(bool enable) { this->log_uart_msg_ = enable; }
void send_1byte_command(unsigned char cmd);
void send_2byte_command(unsigned char cmd, unsigned char high_byte, unsigned char low_byte);
// functions to use in lambdas
void request_physical_limits();
void request_limits();
void request_settings();
void request_move_to();
void step_up();
void step_down();
void stop();
void goto_height(float height);
void goto_min_position();
void goto_max_position();
void goto_mem_position(int pos);
void save_mem_position(int pos);
void goto_height_abs(float height);
void goto_height_pct(float height);
// variables to use in lambdas
std::vector<uint8_t> response_message_;
// uart message variables to use in lambda functions
int getResponseByte(const int index);
protected:
// uart message functions
void decode_response(std::vector<uint8_t> message);
void send_simple_command(unsigned char cmd);
void send_2byte_command(unsigned char cmd, unsigned char high_byte, unsigned char low_byte);
float byte2float(int high, int low);
void log_uart_hex(std::string prefix, std::vector<uint8_t> bytes, uint8_t separator);
void add_button(MaidesiteDeskEntity *button) { buttons_.push_back(button); }
void add_number(MaidesiteDeskEntity *number) { numbers_.push_back(number); }
void add_sensor(MaidesiteDeskEntity *sensor) { sensors_.push_back(sensor); }
// options variables
bool log_uart_msg_ { false };
// uart message variables
std::vector<uint8_t> request_message_;
uint8_t response_payload_length_;
uint8_t request_payload_length_;
bool response_receiving_ { false };
bool request_receiving_ { false };
// sensor variables
float current_height_ = 0;
float limit_min_ = 0;
float limit_max_ = 0;
float physical_min_ = 0;
float physical_max_ = 0;
protected:
// uart message functions
void read_response();
void decode_response(std::vector<uint8_t> message);
void log_uart_hex(std::string prefix, std::vector<uint8_t> bytes, uint8_t separator);
// options variables
bool log_uart_msg_ { false };
// uart message variables
bool checksum_passed_ { false };
bool response_receiving_ { false };
bool request_receiving_ { false };
uint8_t byte_;
uint8_t checksum_;
uint8_t response_payload_length_;
uint8_t request_payload_length_;
std::vector<uint8_t> request_message_;
std::vector<uint8_t> response_message_;
std::vector<uint8_t> desk_message_;
std::vector<MaidesiteDeskEntity *> buttons_;
std::vector<MaidesiteDeskEntity *> numbers_;
std::vector<MaidesiteDeskEntity *> sensors_;
};
} // namespace maidesite_desk
} // namespace esphome

View File

@ -4,6 +4,9 @@ from esphome.components import number
from esphome.const import (
UNIT_CENTIMETER,
UNIT_PERCENT,
CONF_MIN_VALUE,
CONF_MAX_VALUE,
CONF_STEP,
)
from .. import CONF_MAIDESITE_DESK_ID, MaidesiteDeskComponent, maidesite_desk_ns
@ -16,39 +19,48 @@ TYPES = [
CONF_HEIGHT_PCT,
]
CONF_NUMBERS = [
[ 0, 120, .2, ],
[ 0, 100, .1, ],
]
def number_options(min_val, max_val, step) -> cv.Schema:
schema = cv.Schema({
cv.Optional(CONF_MIN_VALUE, default=min_val): cv.float_,
cv.Optional(CONF_MAX_VALUE, default=max_val): cv.float_,
cv.Optional(CONF_STEP, default=step): cv.float_range(min=0.1, max=10.0),
})
return schema
MaidesiteDeskNumber = maidesite_desk_ns.class_("MaidesiteDeskNumber", number.Number, cg.Component)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_MAIDESITE_DESK_ID): cv.use_id(MaidesiteDeskComponent),
cv.GenerateID(CONF_MAIDESITE_DESK_ID): cv.use_id(
MaidesiteDeskComponent
),
cv.Optional(CONF_HEIGHT_ABS): number.number_schema(
MaidesiteDeskNumber,
unit_of_measurement=UNIT_CENTIMETER,
).extend(
number_options(0.0, 120.0, 0.2)
),
cv.Optional(CONF_HEIGHT_PCT): number.number_schema(
MaidesiteDeskNumber,
unit_of_measurement=UNIT_PERCENT,
).extend(
number_options(0.0, 100.0, 0.1)
),
}
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):
hub = await cg.get_variable(config[CONF_MAIDESITE_DESK_ID])
parent = await cg.get_variable(config[CONF_MAIDESITE_DESK_ID])
for index, key in enumerate(TYPES):
if child_config := config.get(key):
var = await number.new_number(
child_config,
min_value=CONF_NUMBERS[index][0],
max_value=CONF_NUMBERS[index][1],
step=CONF_NUMBERS[index][2]
min_value=child_config[CONF_MIN_VALUE],
max_value=child_config[CONF_MAX_VALUE],
step=child_config[CONF_STEP],
)
await cg.register_component(var, child_config)
await cg.register_parented(var, config[CONF_MAIDESITE_DESK_ID])
cg.add(getattr(hub, f"set_{key}_number")(var))
cg.add(var.set_parent(parent))
cg.add(var.set_id(index))
cg.add(parent.add_number(var))

View File

@ -1,5 +1,4 @@
#include "maidesite_desk_number.h"
#include "esphome/core/log.h"
namespace esphome
@ -16,8 +15,50 @@ namespace esphome
void MaidesiteDeskNumber::control(float value)
{
this->publish_state(value);
this->parent_->number_control(this, value);
switch (this->id_)
{
case NumberIds::CONF_HEIGHT_ABS:
{
this->parent_->goto_height_abs(value);
break;
}
case NumberIds::CONF_HEIGHT_PCT:
{
this->parent_->goto_height_pct(value);
break;
}
default: return;
};
this->publish_state(state);
}
void MaidesiteDeskNumber::publish_new_state(const std::vector<uint8_t>& data)
{
if (data.empty()) return;
float new_state;
switch (this->id_)
{
case NumberIds::CONF_HEIGHT_ABS:
{
if (data[2] != 0x01 && data[2] != 0x21 && data[2] != 0x22) return;
new_state = this->byte2float(data[4], data[5]);
if (this->has_state() && this->state == new_state) return;
break;
}
case NumberIds::CONF_HEIGHT_PCT:
{
if (data[2] != 0x01 && data[2] != 0x21 && data[2] != 0x22) return;
new_state = this->byte2float(data[4], data[5]);
new_state = roundf((new_state - this->parent_->limit_min_) / (this->parent_->limit_max_ - this->parent_->limit_min_) * 1000) / 10;
if (this->has_state() && this->state == new_state) return;
break;
}
default: return;
};
this->publish_state(new_state);
}
} // namespace maidesite_desk
} // namespace esphome

View File

@ -8,16 +8,22 @@ namespace esphome
{
namespace maidesite_desk
{
class MaidesiteDeskNumber : public number::Number, public Component, public Parented<MaidesiteDeskComponent>
enum NumberIds : uint8_t
{
CONF_HEIGHT_ABS,
CONF_HEIGHT_PCT,
};
class MaidesiteDeskNumber : public number::Number, public Component,
public Parented<MaidesiteDeskComponent>, public MaidesiteDeskEntity
{
public:
MaidesiteDeskNumber() = default;
void dump_config() override;
void set_id(int id) { this->id_ = id; }
void publish_new_state(const std::vector<uint8_t>& data) override;
protected:
void control(float value) override;
int id_;
};
} // namespace maidesite_desk
} // namespace esphome

View File

@ -2,89 +2,115 @@ import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import sensor
from esphome.const import (
STATE_CLASS_MEASUREMENT,
UNIT_CENTIMETER,
UNIT_PERCENT,
)
from .. import CONF_MAIDESITE_DESK_ID, MaidesiteDeskComponent, maidesite_desk_ns
CONF_UNIT = "unit"
CONF_HEIGHT_ABS = "height_abs"
CONF_HEIGHT_PCT = "height_pct"
CONF_HEIGHT_MIN = "height_min"
CONF_HEIGHT_MAX = "height_max"
CONF_HEIGHT_PHYS_MIN = "height_phys_min"
CONF_HEIGHT_PHYS_MAX = "height_phys_max"
CONF_HEIGHT_LIMIT_MIN = "height_limit_min"
CONF_HEIGHT_LIMIT_MAX = "height_limit_max"
CONF_POSITION_M1 = "position_m1"
CONF_POSITION_M2 = "position_m2"
CONF_POSITION_M3 = "position_m3"
CONF_POSITION_M4 = "position_m4"
CONF_UNIT = "unit"
TYPES = [
CONF_UNIT,
CONF_HEIGHT_ABS,
CONF_HEIGHT_PCT,
CONF_HEIGHT_MIN,
CONF_HEIGHT_MAX,
CONF_HEIGHT_PHYS_MIN,
CONF_HEIGHT_PHYS_MAX,
CONF_HEIGHT_LIMIT_MIN,
CONF_HEIGHT_LIMIT_MAX,
CONF_POSITION_M1,
CONF_POSITION_M2,
CONF_POSITION_M3,
CONF_POSITION_M4,
CONF_UNIT,
]
MaidesiteDeskSensor = maidesite_desk_ns.class_("MaidesiteDeskSensor", sensor.Sensor, cg.Component)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_MAIDESITE_DESK_ID): cv.use_id(MaidesiteDeskComponent),
cv.Optional(CONF_UNIT): sensor.sensor_schema(),
cv.GenerateID(CONF_MAIDESITE_DESK_ID): cv.use_id(
MaidesiteDeskComponent
),
cv.Optional(CONF_HEIGHT_ABS): sensor.sensor_schema(
MaidesiteDeskSensor,
accuracy_decimals=2,
state_class=STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CENTIMETER,
),
cv.Optional(CONF_HEIGHT_PCT): sensor.sensor_schema(
MaidesiteDeskSensor,
accuracy_decimals=2,
state_class=STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_PERCENT,
),
cv.Optional(CONF_HEIGHT_MIN): sensor.sensor_schema(
cv.Optional(CONF_HEIGHT_PHYS_MIN): sensor.sensor_schema(
MaidesiteDeskSensor,
accuracy_decimals=2,
state_class=STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CENTIMETER,
),
cv.Optional(CONF_HEIGHT_MAX): sensor.sensor_schema(
cv.Optional(CONF_HEIGHT_PHYS_MAX): sensor.sensor_schema(
MaidesiteDeskSensor,
accuracy_decimals=2,
state_class=STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CENTIMETER,
),
cv.Optional(CONF_HEIGHT_LIMIT_MIN): sensor.sensor_schema(
MaidesiteDeskSensor,
accuracy_decimals=2,
state_class=STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CENTIMETER,
),
cv.Optional(CONF_HEIGHT_LIMIT_MAX): sensor.sensor_schema(
MaidesiteDeskSensor,
accuracy_decimals=2,
state_class=STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CENTIMETER,
),
cv.Optional(CONF_POSITION_M1): sensor.sensor_schema(
MaidesiteDeskSensor,
accuracy_decimals=2,
state_class=STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CENTIMETER,
),
cv.Optional(CONF_POSITION_M2): sensor.sensor_schema(
MaidesiteDeskSensor,
accuracy_decimals=2,
state_class=STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CENTIMETER,
),
cv.Optional(CONF_POSITION_M3): sensor.sensor_schema(
MaidesiteDeskSensor,
accuracy_decimals=2,
state_class=STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CENTIMETER,
),
cv.Optional(CONF_POSITION_M4): sensor.sensor_schema(
MaidesiteDeskSensor,
accuracy_decimals=2,
state_class=STATE_CLASS_MEASUREMENT,
unit_of_measurement=UNIT_CENTIMETER,
),
}
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):
hub = await cg.get_variable(config[CONF_MAIDESITE_DESK_ID])
for key in TYPES:
parent = await cg.get_variable(config[CONF_MAIDESITE_DESK_ID])
for index, key in enumerate(TYPES):
if child_config := config.get(key):
var = await sensor.new_sensor(child_config)
await cg.register_component(var, child_config)
await cg.register_parented(var, config[CONF_MAIDESITE_DESK_ID])
cg.add(getattr(hub, f"set_{key}_sensor")(var))
cg.add(var.set_parent(parent))
cg.add(var.set_id(index))
cg.add(parent.add_sensor(var))

View File

@ -1,5 +1,4 @@
#include "maidesite_desk_sensor.h"
#include "esphome/core/log.h"
namespace esphome
@ -13,5 +12,107 @@ namespace esphome
LOG_SENSOR("", "Maidesite Desk Sensor", this);
delay(10);
}
void MaidesiteDeskSensor::publish_new_state(const std::vector<uint8_t>& data)
{
if (data.empty()) return;
float new_state;
switch (this->id_)
{
case SensorIds::CONF_HEIGHT_ABS:
{
if (data[2] != 0x01 && data[2] != 0x21 && data[2] != 0x22) return;
new_state = this->byte2float(data[4], data[5]);
if (this->has_state() && this->state == new_state) return;
break;
}
case SensorIds::CONF_HEIGHT_PCT:
{
if (data[2] != 0x01 && data[2] != 0x21 && data[2] != 0x22) return;
new_state = this->byte2float(data[4], data[5]);
new_state = (new_state - this->parent_->limit_min_) / (this->parent_->limit_max_ - this->parent_->limit_min_) * 100;
if (this->has_state() && this->state == new_state) return;
break;
}
case SensorIds::CONF_HEIGHT_PHYS_MIN:
{
if (data[2] != 0x07) return;
new_state = this->byte2float(data[6], data[7]);
this->parent_->physical_min_ = new_state;
if (this->has_state() && this->get_state() == new_state) return;
break;
}
case SensorIds::CONF_HEIGHT_PHYS_MAX:
{
if (data[2] != 0x07) return;
new_state = this->byte2float(data[4], data[5]);
this->parent_->physical_max_ = new_state;
if (this->has_state() && this->get_state() == new_state) return;
break;
}
case SensorIds::CONF_HEIGHT_LIMIT_MIN:
{
if (data[2] == 0x20 && (data[4] >> 4) == 0)
{
// high nibble 0 -> no min limit, use physical_min_
new_state = this->parent_->physical_min_;
}
if (data[2] == 0x22)
{
new_state = this->byte2float(data[4], data[5]);
}
this->parent_->limit_min_ = new_state;
if (this->has_state() && this->get_state() == new_state) return;
break;
}
case SensorIds::CONF_HEIGHT_LIMIT_MAX:
{
if (data[2] == 0x20 && (data[4] & 1) == 0)
{
// low nibble 0 -> no max limit, use physical_max_
new_state = this->parent_->physical_max_;
}
if (data[2] == 0x21)
{
new_state = this->byte2float(data[4], data[5]);
}
this->parent_->limit_max_ = new_state;
if (this->has_state() && this->get_state() == new_state) return;
break;
}
case SensorIds::CONF_POSITION_M1:
{
if (data[2] != 0x25) return;
new_state = this->byte2float(data[4], data[5]);
if (this->has_state() && this->get_state() == new_state) return;
break;
}
case SensorIds::CONF_POSITION_M2:
{
if (data[2] != 0x26) return;
new_state = this->byte2float(data[4], data[5]);
if (this->has_state() && this->get_state() == new_state) return;
break;
}
case SensorIds::CONF_POSITION_M3:
{
if (data[2] != 0x27) return;
new_state = this->byte2float(data[4], data[5]);
if (this->has_state() && this->get_state() == new_state) return;
break;
}
case SensorIds::CONF_POSITION_M4:
{
if (data[2] != 0x28) return;
new_state = this->byte2float(data[4], data[5]);
if (this->has_state() && this->get_state() == new_state) return;
break;
}
default: return;
};
this->publish_state(new_state);
}
} // namespace maidesite_desk
} // namespace esphome

View File

@ -6,13 +6,29 @@
namespace esphome
{
enum SensorIds : uint8_t
{
CONF_HEIGHT_ABS,
CONF_HEIGHT_PCT,
CONF_HEIGHT_PHYS_MIN,
CONF_HEIGHT_PHYS_MAX,
CONF_HEIGHT_LIMIT_MIN,
CONF_HEIGHT_LIMIT_MAX,
CONF_POSITION_M1,
CONF_POSITION_M2,
CONF_POSITION_M3,
CONF_POSITION_M4,
};
namespace maidesite_desk
{
class MaidesiteDeskSensor : public sensor::Sensor, public Component, public Parented<MaidesiteDeskComponent>
class MaidesiteDeskSensor : public sensor::Sensor, public Component,
public Parented<MaidesiteDeskComponent>, public MaidesiteDeskEntity
{
public:
MaidesiteDeskSensor() = default;
void dump_config() override;
void publish_new_state(const std::vector<uint8_t>& data) override;
};
} // namespace maidesite_desk
} // namespace esphome