diff --git a/components/maidesite_desk/__init__.py b/components/maidesite_desk/__init__.py index b2aa354..73023b7 100644 --- a/components/maidesite_desk/__init__.py +++ b/components/maidesite_desk/__init__.py @@ -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])) diff --git a/components/maidesite_desk/button/__init__.py b/components/maidesite_desk/button/__init__.py index b4ee6fa..018e9c8 100644 --- a/components/maidesite_desk/button/__init__.py +++ b/components/maidesite_desk/button/__init__.py @@ -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)) diff --git a/components/maidesite_desk/button/maidesite_desk_button.cpp b/components/maidesite_desk/button/maidesite_desk_button.cpp index a8902f4..3890c7e 100644 --- a/components/maidesite_desk/button/maidesite_desk_button.cpp +++ b/components/maidesite_desk/button/maidesite_desk_button.cpp @@ -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 diff --git a/components/maidesite_desk/button/maidesite_desk_button.h b/components/maidesite_desk/button/maidesite_desk_button.h index 75cfe12..eb49a05 100644 --- a/components/maidesite_desk/button/maidesite_desk_button.h +++ b/components/maidesite_desk/button/maidesite_desk_button.h @@ -8,16 +8,33 @@ namespace esphome { namespace maidesite_desk { - class MaidesiteDeskButton : public button::Button, public Component, public Parented + 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, public MaidesiteDeskEntity { public: MaidesiteDeskButton() = default; void dump_config() override; - void set_id(int id) { this->id_ = id; } + void publish_new_state(const std::vector& data) override {} protected: void press_action() override; - int id_; }; } // namespace maidesite_desk } // namespace esphome diff --git a/components/maidesite_desk/maidesite_desk.cpp b/components/maidesite_desk/maidesite_desk.cpp index d73e562..29209fc 100644 --- a/components/maidesite_desk/maidesite_desk.cpp +++ b/components/maidesite_desk/maidesite_desk.cpp @@ -10,69 +10,92 @@ 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(); this->request_settings(); } - + 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 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((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(); @@ -247,151 +175,51 @@ namespace esphome this->log_uart_hex(">>>", this->request_message_, ','); 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 diff --git a/components/maidesite_desk/maidesite_desk.h b/components/maidesite_desk/maidesite_desk.h index f1ca6fd..2116156 100644 --- a/components/maidesite_desk/maidesite_desk.h +++ b/components/maidesite_desk/maidesite_desk.h @@ -1,19 +1,10 @@ #pragma once +#include +#include #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 -#include #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& data) = 0; + + protected: + int id_ { -1 }; + float byte2float(int high, int low) { return static_cast((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); + + // uart message variables to use in lambda functions + int getResponseByte(const int index); - // variables to use in lambdas - std::vector response_message_; - - protected: - // uart message functions - void decode_response(std::vector 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 bytes, uint8_t separator); - - // options variables - bool log_uart_msg_ { false }; - // uart message variables - std::vector request_message_; - uint8_t response_payload_length_; - uint8_t request_payload_length_; - bool response_receiving_ { false }; - bool request_receiving_ { false }; + 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); } + // 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 message); + void log_uart_hex(std::string prefix, std::vector 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 request_message_; + std::vector response_message_; + std::vector desk_message_; + + std::vector buttons_; + std::vector numbers_; + std::vector sensors_; }; } // namespace maidesite_desk } // namespace esphome diff --git a/components/maidesite_desk/number/__init__.py b/components/maidesite_desk/number/__init__.py index 496cdaa..d99979d 100644 --- a/components/maidesite_desk/number/__init__.py +++ b/components/maidesite_desk/number/__init__.py @@ -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)) diff --git a/components/maidesite_desk/number/maidesite_desk_number.cpp b/components/maidesite_desk/number/maidesite_desk_number.cpp index 82335aa..423aff4 100644 --- a/components/maidesite_desk/number/maidesite_desk_number.cpp +++ b/components/maidesite_desk/number/maidesite_desk_number.cpp @@ -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& 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 diff --git a/components/maidesite_desk/number/maidesite_desk_number.h b/components/maidesite_desk/number/maidesite_desk_number.h index 53bd72a..0d384a8 100644 --- a/components/maidesite_desk/number/maidesite_desk_number.h +++ b/components/maidesite_desk/number/maidesite_desk_number.h @@ -8,16 +8,22 @@ namespace esphome { namespace maidesite_desk { - class MaidesiteDeskNumber : public number::Number, public Component, public Parented + enum NumberIds : uint8_t + { + CONF_HEIGHT_ABS, + CONF_HEIGHT_PCT, + }; + + class MaidesiteDeskNumber : public number::Number, public Component, + public Parented, public MaidesiteDeskEntity { public: MaidesiteDeskNumber() = default; void dump_config() override; - void set_id(int id) { this->id_ = id; } + void publish_new_state(const std::vector& data) override; protected: void control(float value) override; - int id_; }; } // namespace maidesite_desk } // namespace esphome diff --git a/components/maidesite_desk/sensor/__init__.py b/components/maidesite_desk/sensor/__init__.py index dd43a40..837cf5f 100644 --- a/components/maidesite_desk/sensor/__init__.py +++ b/components/maidesite_desk/sensor/__init__.py @@ -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)) diff --git a/components/maidesite_desk/sensor/maidesite_desk_sensor.cpp b/components/maidesite_desk/sensor/maidesite_desk_sensor.cpp index 275a9da..4b20d72 100644 --- a/components/maidesite_desk/sensor/maidesite_desk_sensor.cpp +++ b/components/maidesite_desk/sensor/maidesite_desk_sensor.cpp @@ -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& 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 diff --git a/components/maidesite_desk/sensor/maidesite_desk_sensor.h b/components/maidesite_desk/sensor/maidesite_desk_sensor.h index 1800ada..a1e0a78 100644 --- a/components/maidesite_desk/sensor/maidesite_desk_sensor.h +++ b/components/maidesite_desk/sensor/maidesite_desk_sensor.h @@ -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 + class MaidesiteDeskSensor : public sensor::Sensor, public Component, + public Parented, public MaidesiteDeskEntity { public: MaidesiteDeskSensor() = default; void dump_config() override; + void publish_new_state(const std::vector& data) override; }; } // namespace maidesite_desk } // namespace esphome