diff --git a/components/maidsite_desk_controller/README.md b/components/maidsite_desk/README.md similarity index 100% rename from components/maidsite_desk_controller/README.md rename to components/maidsite_desk/README.md diff --git a/components/maidsite_desk/__init__.py b/components/maidsite_desk/__init__.py new file mode 100644 index 0000000..0bee930 --- /dev/null +++ b/components/maidsite_desk/__init__.py @@ -0,0 +1,29 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome import pins +from esphome.components import uart +from esphome.const import CONF_ID + +CODEOWNERS = ['@elvit'] +MULTICONF = True +DEPENDENCIES = ['uart'] + +CONF_MAIDSITE_DESK_ID = "maidsite_desk" + +maidsite_desk_ns = cg.esphome_ns.namespace('maidsite_desk') +MaidsiteDeskComponent = maidsite_desk_ns.class_('MaidsiteDeskComponent', cg.Component, uart.UARTDevice) + +CONFIG_SCHEMA = ( + cv.Schema( + { + cv.GenerateID(): cv.declare_id(MaidsiteDeskComponent), + } + ) + .extend(uart.UART_DEVICE_SCHEMA) + .extend(cv.COMPONENT_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) diff --git a/components/maidsite_desk/button/__init__.py b/components/maidsite_desk/button/__init__.py new file mode 100644 index 0000000..09ed274 --- /dev/null +++ b/components/maidsite_desk/button/__init__.py @@ -0,0 +1,101 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import button +from esphome.const import ( + ENTITY_CATEGORY_CONFIG, +) +from .. import CONF_MAIDSITE_DESK_ID, MaidsiteDeskComponent, maidsite_desk_ns + + +CONF_STEP_UP = "step_up" +CONF_STEP_DOWN = "step_down" +CONF_STOP = "stop" +CONF_GOTO_MAX = "goto_max" +CONF_GOTO_MIN = "goto_min" +CONF_GOTO_M1 = "goto_m1" +CONF_GOTO_M2 = "goto_m2" +CONF_GOTO_M3 = "goto_m3" +CONF_GOTO_M4 = "goto_m4" +CONF_SAVE_M1 = "save_m1" +CONF_SAVE_M2 = "save_m2" +CONF_SAVE_M3 = "save_m3" +CONF_SAVE_M4 = "save_m4" + +TYPES = [ + 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, +] + +MaidsiteDeskButton = maidsite_desk_ns.class_("MaidsiteDeskButton", button.Button) + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_MAIDSITE_DESK_ID): cv.use_id(MaidsiteDeskComponent), + + cv.Optional(CONF_STEP_UP): button.button_schema( + MaidsiteDeskButton, + ), + cv.Optional(CONF_STEP_DOWN): button.button_schema( + MaidsiteDeskButton, + ), + cv.Optional(CONF_STOP): button.button_schema( + MaidsiteDeskButton, + ), + cv.Optional(CONF_GOTO_MAX): button.button_schema( + MaidsiteDeskButton, + ), + cv.Optional(CONF_GOTO_MIN): button.button_schema( + MaidsiteDeskButton, + ), + cv.Optional(CONF_GOTO_M1): button.button_schema( + MaidsiteDeskButton, + ), + cv.Optional(CONF_GOTO_M2): button.button_schema( + MaidsiteDeskButton, + ), + cv.Optional(CONF_GOTO_M3): button.button_schema( + MaidsiteDeskButton, + ), + cv.Optional(CONF_GOTO_M4): button.button_schema( + MaidsiteDeskButton, + ), + cv.Optional(CONF_SAVE_M1): button.button_schema( + MaidsiteDeskButton, + entity_category=ENTITY_CATEGORY_CONFIG, + ), + cv.Optional(CONF_SAVE_M2): button.button_schema( + MaidsiteDeskButton, + entity_category=ENTITY_CATEGORY_CONFIG, + ), + cv.Optional(CONF_SAVE_M3): button.button_schema( + MaidsiteDeskButton, + entity_category=ENTITY_CATEGORY_CONFIG, + ), + cv.Optional(CONF_SAVE_M4): button.button_schema( + MaidsiteDeskButton, + entity_category=ENTITY_CATEGORY_CONFIG, + ), + } +) + +async def to_code(config): + hub = await cg.get_variable(config[CONF_MAIDSITE_DESK_ID]) + for key in TYPES: + await setup_conf(config, key, hub) + +async def setup_conf(parent_config, key, hub): + if child_config := parent_config.get(key): + var = await button.new_button(child_config) + await cg.register_parented(var, parent_config[CONF_MAIDSITE_DESK_ID]) + cg.add(getattr(hub, f"set_{key}_button")(var)) diff --git a/components/maidsite_desk/button/maidsite_desk_button.cpp b/components/maidsite_desk/button/maidsite_desk_button.cpp new file mode 100644 index 0000000..2e51341 --- /dev/null +++ b/components/maidsite_desk/button/maidsite_desk_button.cpp @@ -0,0 +1,13 @@ +#include "maidsite_desk_button.h" + + +namespace esphome +{ + namespace maidsite_desk + { + void MaidsiteDeskButton::press_action() + { + this->parent_->button_press_action(this); + } + } // namespace maidsite_desk +} // namespace esphome diff --git a/components/maidsite_desk/button/maidsite_desk_button.h b/components/maidsite_desk/button/maidsite_desk_button.h new file mode 100644 index 0000000..db96b6c --- /dev/null +++ b/components/maidsite_desk/button/maidsite_desk_button.h @@ -0,0 +1,19 @@ +#pragma once +#include "esphome/components/button/button.h" +#include "../maidsite_desk.h" + + +namespace esphome +{ + namespace maidsite_desk + { + class MaidsiteDeskButton : public button::Button, public Parented + { + public: + MaidsiteDeskButton() = default; + + protected: + void press_action() override; + }; + } // namespace maidsite_desk +} // namespace esphome diff --git a/components/maidsite_desk_controller/example_basic.yaml b/components/maidsite_desk/example_basic.yaml similarity index 100% rename from components/maidsite_desk_controller/example_basic.yaml rename to components/maidsite_desk/example_basic.yaml diff --git a/components/maidsite_desk_controller/example_full.yaml b/components/maidsite_desk/example_full.yaml similarity index 100% rename from components/maidsite_desk_controller/example_full.yaml rename to components/maidsite_desk/example_full.yaml diff --git a/components/maidsite_desk/maidsite_desk.cpp b/components/maidsite_desk/maidsite_desk.cpp new file mode 100644 index 0000000..756d41b --- /dev/null +++ b/components/maidsite_desk/maidsite_desk.cpp @@ -0,0 +1,459 @@ +#include "maidsite_desk.h" + +#define BUTTON_UNIT "unit" +#define BUTTON_STEP_UP "step_up" +#define BUTTON_STEP_DOWN "step_down" +#define BUTTON_STOP "stop" +#define BUTTON_GOTO_MAX "goto_max" +#define BUTTON_GOTO_MIN "goto_min" +#define BUTTON_GOTO_M1 "goto_m1" +#define BUTTON_GOTO_M2 "goto_m2" +#define BUTTON_GOTO_M3 "goto_m3" +#define BUTTON_GOTO_M4 "goto_m4" +#define BUTTON_SAVE_M1 "save_m1" +#define BUTTON_SAVE_M2 "save_m2" +#define BUTTON_SAVE_M3 "save_m3" +#define BUTTON_SAVE_M4 "save_m4" + +#define NUMBER_HEIGHT_ABS "height_abs" +#define NUMBER_HEIGHT_PCT "height_pct" + + +namespace esphome +{ + namespace maidsite_desk + { + static const char *const TAG = "maidsite_desk"; + + void MaidsiteDeskComponent::dump_config() + { + ESP_LOGCONFIG(TAG, "Maidsite Desk"); + +#ifdef USE_SENSOR + LOG_SENSOR("", "Maidsite Desk Sensor", this->unit_sensor_); + LOG_SENSOR("", "Maidsite Desk Sensor", this->height_abs_sensor_); + LOG_SENSOR("", "Maidsite Desk Sensor", this->height_pct_sensor_); + LOG_SENSOR("", "Maidsite Desk Sensor", this->height_min_sensor_); + LOG_SENSOR("", "Maidsite Desk Sensor", this->height_max_sensor_); + LOG_SENSOR("", "Maidsite Desk Sensor", this->position_m1_sensor_); + LOG_SENSOR("", "Maidsite Desk Sensor", this->position_m2_sensor_); + LOG_SENSOR("", "Maidsite Desk Sensor", this->position_m3_sensor_); + LOG_SENSOR("", "Maidsite Desk Sensor", this->position_m4_sensor_); +#endif +#ifdef USE_BUTTON + LOG_BUTTON("", "Maidsite Desk Button", this->unit_button_); + LOG_BUTTON("", "Maidsite Desk Button", this->step_up_button_); + LOG_BUTTON("", "Maidsite Desk Button", this->step_down_button_); + LOG_BUTTON("", "Maidsite Desk Button", this->stop_button_); + LOG_BUTTON("", "Maidsite Desk Button", this->goto_max_button_); + LOG_BUTTON("", "Maidsite Desk Button", this->goto_min_button_); + LOG_BUTTON("", "Maidsite Desk Button", this->goto_m1_button_); + LOG_BUTTON("", "Maidsite Desk Button", this->goto_m2_button_); + LOG_BUTTON("", "Maidsite Desk Button", this->goto_m3_button_); + LOG_BUTTON("", "Maidsite Desk Button", this->goto_m4_button_); + LOG_BUTTON("", "Maidsite Desk Button", this->save_m1_button_); + LOG_BUTTON("", "Maidsite Desk Button", this->save_m2_button_); + LOG_BUTTON("", "Maidsite Desk Button", this->save_m3_button_); + LOG_BUTTON("", "Maidsite Desk Button", this->save_m4_button_); + LOG_BUTTON("", "Maidsite Desk Button", this->save_m4_button_); +#endif +#ifdef USE_NUMBER + LOG_NUMBER("", "Maidsite Desk Number", this->height_abs_number_); + LOG_NUMBER("", "Maidsite Desk Number", this->height_pct_number_); +#endif + } + + void MaidsiteDeskComponent::setup() + { + ESP_LOGCONFIG(TAG, "Setting up maidsite Desk ..."); + delay(10); + + height_abs_number_->traits.set_min_value(0); + height_abs_number_->traits.set_max_value(120); + height_abs_number_->traits.set_step(.2); + height_pct_number_->traits.set_min_value(0); + height_pct_number_->traits.set_max_value(120); + height_pct_number_->traits.set_step(.1); + + request_physical_limits(); + request_limits(); + request_settings(); + } + + void MaidsiteDeskComponent::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. + // 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); + + // Message shall start with 0xF2, if not skip this byte + if (!this->response_receiving_) + { + if (oneByte != 0xF2) + continue; + this->response_receiving_ = true; + } + // 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) + { + ESP_LOGW(TAG, "Invalid response message: 2. byte is 0x%02X but expexted is 0xF2", oneByte); + delay(10); + this->response_message_.clear(); + this->response_receiving_ = false; + continue; + } + // 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); + this->response_message_.clear(); + this->response_receiving_ = false; + continue; + } + + // Decode message if message is complete (last byte shall be 0x7E) + if (oneByte == 0x7E) + { + this->log_uart_hex("<<<", this->response_message_, ','); + this->decode_response(this->response_message_); + this->response_message_.clear(); + this->response_receiving_ = false; + } + } + } + + void MaidsiteDeskComponent::log_uart_hex(std::string prefix, std::vector bytes, uint8_t separator) + { + std::string logStr; + logStr = "[" + std::to_string(bytes.size()) + "]"; + ESP_LOGD(TAG, "%s %s", prefix.c_str(), logStr.c_str()); + delay(10); + + logStr = ""; + char buffer[5]; + for (size_t i = 0; i < bytes.size(); i++) + { + if (i > 0) logStr += separator; + sprintf(buffer, "%02X", bytes[i]); + logStr += buffer; + } + + size_t chunkSize = 90; + for (size_t i = 0; i < logStr.length(); i += chunkSize) + { + ESP_LOGD(TAG, "%s %s", prefix.c_str(), logStr.substr(i, chunkSize).c_str()); + delay(10); + } + } + + void MaidsiteDeskComponent::decode_response(std::vector message) + { + // ToDo: Add checksum check + + switch (message[2]) + { + case 0x01: + ESP_LOGD(TAG, "set height 0x%0X%0X", message[4], message[5]); + delay(10); + + 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_LOGD(TAG, "set limits 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_LOGD(TAG, "set physical limits 0x%02X%02X 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_LOGD(TAG, "set height max 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_LOGD(TAG, "set height min 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_LOGD(TAG, "set position m1 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_LOGD(TAG, "set position m2 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_LOGD(TAG, "set position m3 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_LOGD(TAG, "set position m4 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; + + // case 0x0E: + // ESP_LOGD(TAG, "units 0x%02X", message[4]); + // if (units != nullptr) + // units->publish_state(byte2float(message[4], message[5])); + // break; + + default: + ESP_LOGD(TAG, "Received unknown message"); + delay(10); + } + } + + float MaidsiteDeskComponent::byte2float(int high, int low) + { + return static_cast((high << 8) + low) / 10; + } + + // Write message: + // format: 0xF1 0xF1 [command] [param_count] [[param] ...] [checksum] 0x7E + // checksum: sum of [command], [param_count] and all [param]s + void MaidsiteDeskComponent::send_simple_command(unsigned char cmd) + { + this->request_message_.clear(); + this->request_message_.insert(this->request_message_.end(), { 0xF1, 0xF1, cmd, 0x00, cmd, 0x7E }); + this->write_array(this->request_message_); + this->log_uart_hex(">>>", this->request_message_, ','); + delay(100); // NOLINT + } + + void MaidsiteDeskComponent::send_2byte_command(unsigned char cmd, unsigned char high_byte, unsigned char low_byte) + { + unsigned char checksum = cmd + 2 + high_byte + low_byte; + this->request_message_.clear(); + this->request_message_.insert(this->request_message_.end(), { 0xF1, 0xF1, cmd, 0x02, high_byte, low_byte, checksum, 0x7E }); + this->write_array(this->request_message_); + this->log_uart_hex(">>>", this->request_message_, ','); + delay(100); // NOLINT + } + + void MaidsiteDeskComponent::step_up() + { + this->send_simple_command(0x01); + } + + void MaidsiteDeskComponent::step_down() + { + this->send_simple_command(0x02); + } + + void MaidsiteDeskComponent::stop() + { + this->send_simple_command(0x2B); + } + + void MaidsiteDeskComponent::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 MaidsiteDeskComponent::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 MaidsiteDeskComponent::request_physical_limits() + { + this->send_simple_command(0x0C); + } + + void MaidsiteDeskComponent::request_limits() + { + this->send_simple_command(0x20); + } + + void MaidsiteDeskComponent::request_settings() + { + this->send_simple_command(0x07); + } + + void MaidsiteDeskComponent::request_move_to() + { + this->send_simple_command(0x1B); + } + + void MaidsiteDeskComponent::goto_max_position() + { + this->goto_height(limit_max_); + } + + void MaidsiteDeskComponent::goto_min_position() + { + this->goto_height(limit_min_); + } + + void MaidsiteDeskComponent::goto_height(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(); + } + + void MaidsiteDeskComponent::button_press_action(button::Button* object) + { + ESP_LOGD(TAG, "button %s pressed", object->get_name()); + delay(10); + + 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(); + } + } + + void MaidsiteDeskComponent::number_control(number::Number* object, float value) + { + ESP_LOGD(TAG, "number %s controlled", object->get_name()); + delay(10); + + 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_); + } + } // namespace maidsite_desk +} // namespace esphome diff --git a/components/maidsite_desk/maidsite_desk.h b/components/maidsite_desk/maidsite_desk.h new file mode 100644 index 0000000..fa164dc --- /dev/null +++ b/components/maidsite_desk/maidsite_desk.h @@ -0,0 +1,103 @@ +#pragma once +#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 + + +namespace esphome +{ + namespace maidsite_desk + { + + class MaidsiteDeskComponent : 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); +#endif +#ifdef USE_NUMBER + SUB_NUMBER(height_abs); + SUB_NUMBER(height_pct); +#endif + + MaidsiteDeskComponent() = default; + float get_setup_priority() const override { return setup_priority::LATE; } + void dump_config() override; + void setup() override; + void loop() override; + + void button_press_action(button::Button* object); + void number_control(number::Number* object, float value); + void request_physical_limits(); + void request_limits(); + void request_settings(); + void request_move_to(); + + protected: + 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); + + void step_up(); + void step_down(); + void stop(); + void goto_mem_position(int pos); + void save_mem_position(int pos); + void goto_height(float height); + void goto_max_position(); + void goto_min_position(); + + private: + std::vector response_message_; + std::vector request_message_; + uint8_t response_payload_length_; + uint8_t request_payload_length_; + bool response_receiving_{false}; + bool request_receiving_{false}; + + float current_height_ = 0; + float limit_min_ = 0; + float limit_max_ = 0; + float physical_min_ = 0; + float physical_max_ = 0; + }; + } // namespace maidsite_desk +} // namespace esphome diff --git a/components/maidsite_desk/number/__init__.py b/components/maidsite_desk/number/__init__.py new file mode 100644 index 0000000..6243c7c --- /dev/null +++ b/components/maidsite_desk/number/__init__.py @@ -0,0 +1,45 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import number +from esphome.const import ( + UNIT_CENTIMETER, + UNIT_PERCENT, +) +from .. import CONF_MAIDSITE_DESK_ID, MaidsiteDeskComponent, maidsite_desk_ns + + +CONF_HEIGHT_ABS = "height_abs" +CONF_HEIGHT_PCT = "height_pct" + +TYPES = [ + CONF_HEIGHT_ABS, + CONF_HEIGHT_PCT, +] + +MaidsiteDeskNumber = maidsite_desk_ns.class_("MaidsiteDeskNumber", number.Number) + +CONFIG_SCHEMA = cv.Schema( + { + cv.GenerateID(CONF_MAIDSITE_DESK_ID): cv.use_id(MaidsiteDeskComponent), + + cv.Optional(CONF_HEIGHT_ABS): number.number_schema( + MaidsiteDeskNumber, + unit_of_measurement=UNIT_CENTIMETER, + ), + cv.Optional(CONF_HEIGHT_PCT): number.number_schema( + MaidsiteDeskNumber, + unit_of_measurement=UNIT_PERCENT, + ), + } +) + +async def to_code(config): + hub = await cg.get_variable(config[CONF_MAIDSITE_DESK_ID]) + for key in TYPES: + await setup_conf(config, key, hub) + +async def setup_conf(parent_config, key, hub): + if child_config := parent_config.get(key): + var = await number.new_number(child_config, min_value=0, max_value=100, step=.1) + await cg.register_parented(var, parent_config[CONF_MAIDSITE_DESK_ID]) + cg.add(getattr(hub, f"set_{key}_number")(var)) diff --git a/components/maidsite_desk/number/maidsite_desk_number.cpp b/components/maidsite_desk/number/maidsite_desk_number.cpp new file mode 100644 index 0000000..1953909 --- /dev/null +++ b/components/maidsite_desk/number/maidsite_desk_number.cpp @@ -0,0 +1,14 @@ +#include "maidsite_desk_number.h" + + +namespace esphome +{ + namespace maidsite_desk + { + void MaidsiteDeskNumber::control(float value) + { + this->publish_state(value); + this->parent_->number_control(this, value); + } + } // namespace maidsite_desk +} // namespace esphome diff --git a/components/maidsite_desk/number/maidsite_desk_number.h b/components/maidsite_desk/number/maidsite_desk_number.h new file mode 100644 index 0000000..a586de3 --- /dev/null +++ b/components/maidsite_desk/number/maidsite_desk_number.h @@ -0,0 +1,19 @@ +#pragma once +#include "esphome/components/number/number.h" +#include "../maidsite_desk.h" + + +namespace esphome +{ + namespace maidsite_desk + { + class MaidsiteDeskNumber : public number::Number, public Parented + { + public: + MaidsiteDeskNumber() = default; + + protected: + void control(float value) override; + }; + } // namespace maidsite_desk +} // namespace esphome diff --git a/components/maidsite_desk/sensor.py b/components/maidsite_desk/sensor.py new file mode 100644 index 0000000..4cf3b7d --- /dev/null +++ b/components/maidsite_desk/sensor.py @@ -0,0 +1,82 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor +from esphome.const import ( + UNIT_CENTIMETER, + UNIT_PERCENT, +) +from . import MaidsiteDeskComponent, CONF_MAIDSITE_DESK_ID + +CONF_UNIT = "unit" +CONF_HEIGHT_ABS = "height_abs" +CONF_HEIGHT_PCT = "height_pct" +CONF_HEIGHT_MIN = "height_min" +CONF_HEIGHT_MAX = "height_max" +CONF_POSITION_M1 = "position_m1" +CONF_POSITION_M2 = "position_m2" +CONF_POSITION_M3 = "position_m3" +CONF_POSITION_M4 = "position_m4" + +TYPES = [ + CONF_UNIT, + CONF_HEIGHT_ABS, + CONF_HEIGHT_PCT, + CONF_HEIGHT_MIN, + CONF_HEIGHT_MAX, + CONF_POSITION_M1, + CONF_POSITION_M2, + CONF_POSITION_M3, + CONF_POSITION_M4, +] + +CONFIG_SCHEMA = cv.All( + cv.Schema( + { + cv.GenerateID(CONF_MAIDSITE_DESK_ID): cv.use_id(MaidsiteDeskComponent), + + cv.Optional(CONF_UNIT): sensor.sensor_schema(), + cv.Optional(CONF_HEIGHT_ABS): sensor.sensor_schema( + accuracy_decimals=2, + unit_of_measurement=UNIT_CENTIMETER, + ), + cv.Optional(CONF_HEIGHT_PCT): sensor.sensor_schema( + accuracy_decimals=2, + unit_of_measurement=UNIT_PERCENT, + ), + cv.Optional(CONF_HEIGHT_MIN): sensor.sensor_schema( + accuracy_decimals=2, + unit_of_measurement=UNIT_CENTIMETER, + ), + cv.Optional(CONF_HEIGHT_MAX): sensor.sensor_schema( + accuracy_decimals=2, + unit_of_measurement=UNIT_CENTIMETER, + ), + cv.Optional(CONF_POSITION_M1): sensor.sensor_schema( + accuracy_decimals=2, + unit_of_measurement=UNIT_CENTIMETER, + ), + cv.Optional(CONF_POSITION_M2): sensor.sensor_schema( + accuracy_decimals=2, + unit_of_measurement=UNIT_CENTIMETER, + ), + cv.Optional(CONF_POSITION_M3): sensor.sensor_schema( + accuracy_decimals=2, + unit_of_measurement=UNIT_CENTIMETER, + ), + cv.Optional(CONF_POSITION_M4): sensor.sensor_schema( + accuracy_decimals=2, + unit_of_measurement=UNIT_CENTIMETER, + ), + } + ).extend(cv.COMPONENT_SCHEMA) +) + +async def to_code(config): + hub = await cg.get_variable(config[CONF_MAIDSITE_DESK_ID]) + for key in TYPES: + await setup_conf(config, key, hub) + +async def setup_conf(parent_config, key, hub): + if child_config := parent_config.get(key): + var = await sensor.new_sensor(child_config) + cg.add(getattr(hub, f"set_{key}_sensor")(var)) diff --git a/components/maidsite_desk_controller/stl/D1mini Case Bottom.stl b/components/maidsite_desk/stl/D1mini Case Bottom.stl similarity index 100% rename from components/maidsite_desk_controller/stl/D1mini Case Bottom.stl rename to components/maidsite_desk/stl/D1mini Case Bottom.stl diff --git a/components/maidsite_desk_controller/stl/D1mini Case Clamp.stl b/components/maidsite_desk/stl/D1mini Case Clamp.stl similarity index 100% rename from components/maidsite_desk_controller/stl/D1mini Case Clamp.stl rename to components/maidsite_desk/stl/D1mini Case Clamp.stl diff --git a/components/maidsite_desk_controller/stl/D1mini Case Table Clamp A.stl b/components/maidsite_desk/stl/D1mini Case Table Clamp A.stl similarity index 100% rename from components/maidsite_desk_controller/stl/D1mini Case Table Clamp A.stl rename to components/maidsite_desk/stl/D1mini Case Table Clamp A.stl diff --git a/components/maidsite_desk_controller/stl/D1mini Case Table Clamp B.stl b/components/maidsite_desk/stl/D1mini Case Table Clamp B.stl similarity index 100% rename from components/maidsite_desk_controller/stl/D1mini Case Table Clamp B.stl rename to components/maidsite_desk/stl/D1mini Case Table Clamp B.stl diff --git a/components/maidsite_desk_controller/stl/D1mini Case Top.stl b/components/maidsite_desk/stl/D1mini Case Top.stl similarity index 100% rename from components/maidsite_desk_controller/stl/D1mini Case Top.stl rename to components/maidsite_desk/stl/D1mini Case Top.stl diff --git a/components/maidsite_desk_controller/__init__.py b/components/maidsite_desk_controller/__init__.py deleted file mode 100644 index 43d9e42..0000000 --- a/components/maidsite_desk_controller/__init__.py +++ /dev/null @@ -1,184 +0,0 @@ -import esphome.codegen as cg -import esphome.config_validation as cv -from esphome import pins -from esphome.components import uart, sensor, button, number -from esphome.const import CONF_ID, CONF_UNIT_OF_MEASUREMENT, CONF_STEP, CONF_ACCURACY_DECIMALS, CONF_ENTITY_CATEGORY, UNIT_CENTIMETER, UNIT_PERCENT, ENTITY_CATEGORY_CONFIG - -DEPENDENCIES = ['uart'] -AUTO_LOAD = ['sensor', 'button', 'number'] - -maidsite_desk_controller_ns = cg.esphome_ns.namespace('maidsite_desk_controller') - -MaidsiteDeskController = maidsite_desk_controller_ns.class_('MaidsiteDeskController', cg.Component, uart.UARTDevice) -MaidsiteDeskButton = maidsite_desk_controller_ns.class_('MaidsiteDeskButton', button.Button, cg.Component) -MaidsiteDeskNumber = maidsite_desk_controller_ns.class_('MaidsiteDeskNumber', number.Number, cg.Component) - - -CONF_SENSORS = "sensors" -CONF_BUTTONS = "buttons" -CONF_NUMBERS = "numbers" -CONF_UNIT = "unit" -CONF_STEP_UP = "step_up" -CONF_STEP_DOWN = "step_down" -CONF_STOP = "stop" -CONF_GOTO_MAX = "goto_max" -CONF_GOTO_MIN = "goto_min" -CONF_HEIGHT_MIN = "height_min" -CONF_HEIGHT_MAX = "height_max" -CONF_HEIGHT_ABS = "height_abs" -CONF_HEIGHT_PCT = "height_pct" -CONF_POSITION_M1 = "position_m1" -CONF_POSITION_M2 = "position_m2" -CONF_POSITION_M3 = "position_m3" -CONF_POSITION_M4 = "position_m4" -CONF_GOTO_M1 = "goto_m1" -CONF_GOTO_M2 = "goto_m2" -CONF_GOTO_M3 = "goto_m3" -CONF_GOTO_M4 = "goto_m4" -CONF_SAVE_M1 = "save_m1" -CONF_SAVE_M2 = "save_m2" -CONF_SAVE_M3 = "save_m3" -CONF_SAVE_M4 = "save_m4" - -button_constants = {} -button_constants[CONF_STEP_UP] = 0 -button_constants[CONF_STEP_DOWN] = 1 -button_constants[CONF_STOP] = 2 -button_constants[CONF_GOTO_MAX] = 3 -button_constants[CONF_GOTO_MIN] = 4 -button_constants[CONF_GOTO_M1] = 5 -button_constants[CONF_GOTO_M2] = 6 -button_constants[CONF_GOTO_M3] = 7 -button_constants[CONF_GOTO_M4] = 8 -button_constants[CONF_SAVE_M1] = 9 -button_constants[CONF_SAVE_M2] = 10 -button_constants[CONF_SAVE_M3] = 11 -button_constants[CONF_SAVE_M4] = 12 - -number_constants = {} -number_constants[CONF_HEIGHT_ABS] = 0 -number_constants[CONF_HEIGHT_PCT] = 1 - -CONFIG_SCHEMA = cv.COMPONENT_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(MaidsiteDeskController), - - cv.Optional(CONF_SENSORS): cv.Schema({ - cv.Optional(CONF_UNIT): sensor.sensor_schema(accuracy_decimals = 0), - cv.Optional(CONF_HEIGHT_ABS): sensor.sensor_schema(accuracy_decimals = 1, unit_of_measurement = UNIT_CENTIMETER), - 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_POSITION_M1): sensor.sensor_schema(accuracy_decimals = 1, unit_of_measurement = UNIT_CENTIMETER), - cv.Optional(CONF_POSITION_M2): sensor.sensor_schema(accuracy_decimals = 1, unit_of_measurement = UNIT_CENTIMETER), - cv.Optional(CONF_POSITION_M3): sensor.sensor_schema(accuracy_decimals = 1, unit_of_measurement = UNIT_CENTIMETER), - cv.Optional(CONF_POSITION_M4): sensor.sensor_schema(accuracy_decimals = 1, unit_of_measurement = UNIT_CENTIMETER), - }), - cv.Optional(CONF_NUMBERS): cv.Schema({ - cv.Optional(CONF_HEIGHT_ABS): number.NUMBER_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(MaidsiteDeskNumber), - cv.Optional(CONF_ACCURACY_DECIMALS, default=1): cv.int_, - cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=UNIT_CENTIMETER): cv.string, - cv.Optional(CONF_STEP, default=1): cv.float_, - }), - cv.Optional(CONF_HEIGHT_PCT): number.NUMBER_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(MaidsiteDeskNumber), - cv.Optional(CONF_ACCURACY_DECIMALS, default=1): cv.int_, - cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=UNIT_PERCENT): cv.string, - cv.Optional(CONF_STEP, default=.1): cv.float_, - }), - }), - cv.Optional(CONF_BUTTONS): cv.Schema({ - cv.Optional(CONF_STEP_UP): button.BUTTON_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(MaidsiteDeskButton), - }), - cv.Optional(CONF_STEP_DOWN): button.BUTTON_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(MaidsiteDeskButton), - }), - cv.Optional(CONF_STOP): button.BUTTON_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(MaidsiteDeskButton), - }), - cv.Optional(CONF_GOTO_MAX): button.BUTTON_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(MaidsiteDeskButton), - }), - cv.Optional(CONF_GOTO_MIN): button.BUTTON_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(MaidsiteDeskButton), - }), - cv.Optional(CONF_GOTO_M1): button.BUTTON_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(MaidsiteDeskButton), - }), - cv.Optional(CONF_GOTO_M2): button.BUTTON_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(MaidsiteDeskButton), - }), - cv.Optional(CONF_GOTO_M3): button.BUTTON_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(MaidsiteDeskButton), - }), - cv.Optional(CONF_GOTO_M4): button.BUTTON_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(MaidsiteDeskButton), - }), - cv.Optional(CONF_SAVE_M1): button.BUTTON_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(MaidsiteDeskButton), - cv.Optional(CONF_ENTITY_CATEGORY, default=ENTITY_CATEGORY_CONFIG): cv.entity_category, - }), - cv.Optional(CONF_SAVE_M2): button.BUTTON_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(MaidsiteDeskButton), - cv.Optional(CONF_ENTITY_CATEGORY, default=ENTITY_CATEGORY_CONFIG): cv.entity_category, - }), - cv.Optional(CONF_SAVE_M3): button.BUTTON_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(MaidsiteDeskButton), - cv.Optional(CONF_ENTITY_CATEGORY, default=ENTITY_CATEGORY_CONFIG): cv.entity_category, - }), - cv.Optional(CONF_SAVE_M4): button.BUTTON_SCHEMA.extend({ - cv.GenerateID(): cv.declare_id(MaidsiteDeskButton), - cv.Optional(CONF_ENTITY_CATEGORY, default=ENTITY_CATEGORY_CONFIG): cv.entity_category, - }), - }), -}).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_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_ABS in sensors: - sens = await sensor.new_sensor(sensors[CONF_HEIGHT_ABS]) - cg.add(var.set_sensor_height(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_POSITION_M1 in sensors: - sens = await sensor.new_sensor(sensors[CONF_POSITION_M1]) - cg.add(var.set_sensor_position_m1(sens)) - if CONF_POSITION_M2 in sensors: - sens = await sensor.new_sensor(sensors[CONF_POSITION_M2]) - cg.add(var.set_sensor_position_m2(sens)) - if CONF_POSITION_M3 in sensors: - sens = await sensor.new_sensor(sensors[CONF_POSITION_M3]) - cg.add(var.set_sensor_position_m3(sens)) - if CONF_POSITION_M4 in sensors: - sens = await sensor.new_sensor(sensors[CONF_POSITION_M4]) - cg.add(var.set_sensor_position_m4(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])) - - if CONF_NUMBERS in config: - numbers = config[CONF_NUMBERS] - for number_type in numbers.keys(): - num = await number.new_number(numbers[number_type], min_value=0, max_value=100, step=.1) - cg.add(var.add_number(num, number_constants[number_type])) - diff --git a/components/maidsite_desk_controller/maidsite_desk_controller.cpp b/components/maidsite_desk_controller/maidsite_desk_controller.cpp deleted file mode 100644 index 710d7a2..0000000 --- a/components/maidsite_desk_controller/maidsite_desk_controller.cpp +++ /dev/null @@ -1,381 +0,0 @@ -#include "maidsite_desk_controller.h" -#include "esphome/core/log.h" - -namespace esphome -{ - namespace maidsite_desk_controller - { - static const char *const TAG = "maidsite_desk_controller"; - - float MaidsiteDeskController::byte2float(int high, int low) - { - return static_cast((high << 8) + low) / 10; - } - - bool MaidsiteDeskController::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. - - // Read message: - // 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 MaidsiteDeskController::handleMessage(unsigned int *message) - { - // ESP_LOGV("maidsite_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("maidsite_desk_controller", "sensor_height 0x%0X%0X", message[2], message[3]); - float new_height; - new_height = byte2float(message[2], message[3]); - if (new_height == current_height) - return; - current_height = new_height; - if (sensor_height != nullptr) - sensor_height->publish_state(current_height); - if (number_height_abs != nullptr) - number_height_abs->publish_state(current_height); - - if (sensor_height_pct != nullptr && limit_max != 0) - sensor_height_pct->publish_state((current_height - limit_min) / (limit_max - limit_min) * 100); - if (number_height_pct != nullptr && limit_max != 0) - number_height_pct->publish_state(roundf((current_height - limit_min) / (limit_max - limit_min) * 1000) / 10); - break; - - case 0x20: - ESP_LOGV("maidsite_desk_controller", "sensor_limits 0x%0X max %i min %i", message[2], (message[2] & 1), (message[2] >> 4)); - - if ((message[2] & 1) == 0) - { // low nibble 0 -> no max limit, use physical_max - limit_max = physical_max; - if (sensor_height_max != nullptr) - sensor_height_max->publish_state(limit_max); - if (number_height_abs != nullptr) - number_height_abs->set_max_value(limit_max); - } - if ((message[2] >> 4) == 0) - { // high nibble 0 -> no min limit, use physical_min - limit_min = physical_min; - if (sensor_height_min != nullptr) - sensor_height_min->publish_state(limit_min); - if (number_height_abs != nullptr) - number_height_abs->set_min_value(limit_min); - } - break; - - case 0x07: - ESP_LOGV("maidsite_desk_controller", "sensor_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("maidsite_desk_controller", "sensor_height_max 0x%02X%02X", message[2], message[3]); - limit_max = byte2float(message[2], message[3]); - if (sensor_height_max != nullptr) - sensor_height_max->publish_state(limit_max); - if (number_height_abs != nullptr) - number_height_abs->set_max_value(limit_max); - break; - - case 0x22: - ESP_LOGV("maidsite_desk_controller", "sensor_height_min 0x%02X%02X", message[2], message[3]); - limit_min = byte2float(message[2], message[3]); - if (sensor_height_min != nullptr) - sensor_height_min->publish_state(limit_min); - if (number_height_abs != nullptr) - number_height_abs->set_min_value(limit_min); - break; - - case 0x25: - ESP_LOGV("maidsite_desk_controller", "sensor_position_m1 0x%02X%02X", message[2], message[3]); - if (sensor_position_m1 != nullptr) - sensor_position_m1->publish_state(byte2float(message[2], message[3])); - break; - - case 0x26: - ESP_LOGV("maidsite_desk_controller", "sensor_position_m2 0x%02X%02X", message[2], message[3]); - if (sensor_position_m2 != nullptr) - sensor_position_m2->publish_state(byte2float(message[2], message[3])); - break; - - case 0x27: - ESP_LOGV("maidsite_desk_controller", "sensor_position_m3 0x%02X%02X", message[2], message[3]); - if (sensor_position_m3 != nullptr) - sensor_position_m3->publish_state(byte2float(message[2], message[3])); - break; - - case 0x28: - ESP_LOGV("maidsite_desk_controller", "sensor_position_m4 0x%02X%02X", message[2], message[3]); - if (sensor_position_m4 != nullptr) - sensor_position_m4->publish_state(byte2float(message[2], message[3])); - break; - - // case 0x0E: - // ESP_LOGV("maidsite_desk_controller", "units 0x%02X", message[2]); - // if (units != nullptr) - // units->publish_state(byte2float(message[2], message[3])); - // break; - - default: - ESP_LOGV("maidsite_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 MaidsiteDeskController::update() - { - const int max_length = 10; - static unsigned int buffer[max_length]; - while (available()) - { - if (bufferMessage(read(), buffer, max_length)) - { - handleMessage(buffer); - } - } - } - - // Write message: - // format: 0xF1 0xF1 [command] [param_count] [[param] ...] [checksum] 0x7E - // checksum: sum of [command], [param_count] and all [param]s - void MaidsiteDeskController::send_simple_command(unsigned char cmd) - { - write_array({0xF1, 0xF1, cmd, 0x00, cmd, 0x7E}); - } - - void MaidsiteDeskController::send_2byte_command(unsigned char cmd, unsigned char high_byte, unsigned char low_byte) - { - unsigned char checksum = cmd + 2 + high_byte + low_byte; - write_array({ 0xF1, 0xF1, cmd, 0x02, high_byte, low_byte, checksum, 0x7E }); - } - - void MaidsiteDeskController::step_up() - { - send_simple_command(0x01); - } - - void MaidsiteDeskController::step_down() - { - send_simple_command(0x02); - } - - void MaidsiteDeskController::stop() - { - send_simple_command(0x2B); - } - - void MaidsiteDeskController::goto_mem_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 MaidsiteDeskController::save_mem_position(int pos) - { - switch (pos) - { - case 1: - send_simple_command(0x03); - break; - case 2: - send_simple_command(0x04); - break; - case 3: - send_simple_command(0x25); - break; - case 4: - send_simple_command(0x26); - break; - } - } - - void MaidsiteDeskController::request_physical_limits() - { - send_simple_command(0x0C); - } - - void MaidsiteDeskController::request_limits() - { - send_simple_command(0x20); - } - - void MaidsiteDeskController::request_settings() - { - send_simple_command(0x07); - } - - void MaidsiteDeskController::request_move_to() - { - send_simple_command(0x1B); - } - - void MaidsiteDeskController::goto_height(float height) - { - unsigned char high_byte = ((int)height * 10) >> 8; - unsigned char low_byte = ((int)height * 10) & 0xFF; - - send_2byte_command(0x80, high_byte, low_byte); - request_move_to(); - } - - void MaidsiteDeskController::goto_max_position() - { - goto_height(limit_max); - } - - void MaidsiteDeskController::goto_min_position() - { - goto_height(limit_min); - } - - void MaidsiteDeskController::add_button(button::Button *btn, int action) - { - btn->add_on_press_callback([this, action]() { this->button_press_action(action); }); - } - - void MaidsiteDeskController::button_press_action(int action) - { - ESP_LOGV("MaidsiteDeskController", "button_press_action %i", action); - switch (action) - { - case BUTTON_STEP_UP: - step_up(); - break; - case BUTTON_STEP_DOWN: - step_down(); - break; - case BUTTON_STOP: - stop(); - break; - case BUTTON_GOTO_MAX: - goto_max_position(); - break; - case BUTTON_GOTO_MIN: - goto_min_position(); - break; - case BUTTON_GOTO_M1: - goto_mem_position(1); - break; - case BUTTON_GOTO_M2: - goto_mem_position(2); - break; - case BUTTON_GOTO_M3: - goto_mem_position(3); - break; - case BUTTON_GOTO_M4: - goto_mem_position(4); - break; - case BUTTON_SAVE_M1: - save_mem_position(1); - delay(100); - request_settings(); - break; - case BUTTON_SAVE_M2: - save_mem_position(2); - delay(100); - request_settings(); - break; - case BUTTON_SAVE_M3: - save_mem_position(3); - delay(100); - request_settings(); - break; - case BUTTON_SAVE_M4: - save_mem_position(4); - delay(100); - request_settings(); - break; - } - } - - void MaidsiteDeskController::add_number(MaidsiteDeskNumber *number, int type) - { - switch (type) - { - case NUMBER_HEIGHT: - number_height_abs = number; - break; - case NUMBER_HEIGHT_PCT: - number_height_pct = number; - number_height_pct->set_min_value(0); - number_height_pct->set_max_value(100); - break; - default: - return; - } - number->set_type(type); - number->set_parent(this); - } - - void MaidsiteDeskController::number_control(int type, float value) - { - ESP_LOGV("MaidsiteDeskController", "number_control %i", type); - switch (type) - { - case NUMBER_HEIGHT: - goto_height(value); - break; - case NUMBER_HEIGHT_PCT: - if (limit_max > 0 && limit_max > limit_min) - goto_height((limit_max - limit_min) * value / 100 + limit_min); - break; - } - } - - void MaidsiteDeskButton::press_action() {} - - } // namespace maidsite_desk_controller -} // namespace esphome diff --git a/components/maidsite_desk_controller/maidsite_desk_controller.h b/components/maidsite_desk_controller/maidsite_desk_controller.h deleted file mode 100644 index 237b129..0000000 --- a/components/maidsite_desk_controller/maidsite_desk_controller.h +++ /dev/null @@ -1,105 +0,0 @@ -#pragma once - -#include -#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" -#include "maidsite_desk_number.h" - -#define BUTTON_STEP_UP 0 -#define BUTTON_STEP_DOWN 1 -#define BUTTON_STOP 2 -#define BUTTON_GOTO_MAX 3 -#define BUTTON_GOTO_MIN 4 -#define BUTTON_GOTO_M1 5 -#define BUTTON_GOTO_M2 6 -#define BUTTON_GOTO_M3 7 -#define BUTTON_GOTO_M4 8 -#define BUTTON_SAVE_M1 9 -#define BUTTON_SAVE_M2 10 -#define BUTTON_SAVE_M3 11 -#define BUTTON_SAVE_M4 12 - -#define NUMBER_HEIGHT 0 -#define NUMBER_HEIGHT_PCT 1 - -namespace esphome -{ - namespace maidsite_desk_controller - { - - class MaidsiteDeskController : public PollingComponent, public sensor::Sensor, public uart::UARTDevice - { - private: - float current_height = 0; - float limit_min = 0; - float limit_max = 0; - 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->sensor_height = sensor; } - void set_sensor_height_min(sensor::Sensor *sensor) { this->sensor_height_min = sensor; } - void set_sensor_height_max(sensor::Sensor *sensor) { this->sensor_height_max = sensor; } - void set_sensor_height_pct(sensor::Sensor *sensor) { this->sensor_height_pct = sensor; } - void set_sensor_position_m1(sensor::Sensor *sensor) { this->sensor_position_m1 = sensor; } - void set_sensor_position_m2(sensor::Sensor *sensor) { this->sensor_position_m2 = sensor; } - void set_sensor_position_m3(sensor::Sensor *sensor) { this->sensor_position_m3 = sensor; } - void set_sensor_position_m4(sensor::Sensor *sensor) { this->sensor_position_m4 = sensor; } - - void add_button(button::Button *btn, int action); - void add_number(MaidsiteDeskNumber *number, int type); - - void send_simple_command(unsigned char cmd); - void send_2byte_command(unsigned char cmd, unsigned char high_byte, unsigned char low_byte); - - void step_up(); - void step_down(); - void stop(); - void goto_mem_position(int pos); - void save_mem_position(int pos); - void goto_height(float height); - void goto_max_position(); - void goto_min_position(); - - void request_physical_limits(); - void request_limits(); - void request_settings(); - void request_move_to(); - - void number_control(int type, float value); - - protected: - Sensor *sensor_height{nullptr}; - Sensor *sensor_height_min{nullptr}; - Sensor *sensor_height_max{nullptr}; - Sensor *sensor_height_pct{nullptr}; - - Sensor *sensor_position_m1{nullptr}; - Sensor *sensor_position_m2{nullptr}; - Sensor *sensor_position_m3{nullptr}; - Sensor *sensor_position_m4{nullptr}; - - MaidsiteDeskNumber *number_height_abs{nullptr}; - MaidsiteDeskNumber *number_height_pct{nullptr}; - - void button_press_action(int type); - }; - - class MaidsiteDeskButton : public Component, public button::Button - { - protected: - void press_action() override; - }; - - } // namespace maidsite_desk_controller -} // namespace esphome diff --git a/components/maidsite_desk_controller/maidsite_desk_number.cpp b/components/maidsite_desk_controller/maidsite_desk_number.cpp deleted file mode 100644 index 00a3b49..0000000 --- a/components/maidsite_desk_controller/maidsite_desk_number.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include "maidsite_desk_number.h" -#include "maidsite_desk_controller.h" -#include "esphome/core/log.h" - -namespace esphome -{ - namespace maidsite_desk_controller - { - - static const char *const TAG = "maidsite_desk.number"; - - void MaidsiteDeskNumber::setup() {} - - void MaidsiteDeskNumber::dump_config() - { - LOG_NUMBER("", "MaidsiteDeskNumber", this); - ESP_LOGCONFIG(TAG, " type %i", type); - } - - void MaidsiteDeskNumber::control(float value) - { - this->publish_state(value); - parent->number_control(this->type, value); - } - - void MaidsiteDeskNumber::set_min_value(float value) - { - traits.set_min_value(value); - } - - void MaidsiteDeskNumber::set_max_value(float value) - { - traits.set_max_value(value); - } - - } // namespace maidsite_desk_controller -} // namespace esphome diff --git a/components/maidsite_desk_controller/maidsite_desk_number.h b/components/maidsite_desk_controller/maidsite_desk_number.h deleted file mode 100644 index c537f3d..0000000 --- a/components/maidsite_desk_controller/maidsite_desk_number.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "esphome/core/component.h" -#include "esphome/components/number/number.h" -#include "esphome/core/log.h" - -namespace esphome -{ - namespace maidsite_desk_controller - { - - class MaidsiteDeskController; - - class MaidsiteDeskNumber : public number::Number, public Component - { - private: - int type; - MaidsiteDeskController *parent; - - public: - void setup() override; - void dump_config() override; - - void set_parent(MaidsiteDeskController *parent) { this->parent = parent; } - void set_type(int type) { this->type = type; } - - void set_min_value(float value); - void set_max_value(float value); - - protected: - void control(float value) override; - }; - - } // namespace maidsite_desk_controller -} // namespace esphome