diff --git a/.gitignore b/.gitignore
index 59ee0a5..9afcb24 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1 @@
-**/__pycache__/**
+**/__pycache__/**
diff --git a/.vimrc b/.vimrc
deleted file mode 100644
index 00f4114..0000000
--- a/.vimrc
+++ /dev/null
@@ -1,4 +0,0 @@
-if SpmSetProject("esphome_components") == 0
-    nmap <leader>h yy'fpwd2f:f)lC;<esc>==mf^w
-    nmap <leader>s 02w"aye2b02w"byej/::a
Vf{%jd/::b
f{%jpzz^wj
-endif
diff --git a/README.md b/README.md
index dff4597..c0e1f5b 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-# Rocka84's ESPHome Components
-
-## Available Components
-
-* [Jiecang Desk Controller](components/jiecang_desk_controller/)
-
+# ElVit's ESPHome Components
+
+## Available Components
+
+* [Maidsite Desk Controller](components/maidsite_desk_controller/)
+
diff --git a/components/jiecang_desk_controller/README.md b/components/jiecang_desk_controller/README.md
deleted file mode 100644
index 73386a0..0000000
--- a/components/jiecang_desk_controller/README.md
+++ /dev/null
@@ -1,184 +0,0 @@
-# ESPHome Jiecang Desk Controller
-
-[ESPHome](https://esphome.io/) component for controlling Jiecang desk controllers via their serial protocol.
-
-![](https://github.com/Rocka84/esphome_components/assets/2353088/b04d1fd7-d0ed-443d-87b7-87454757c0f0)
-
-Attention: I only have one [RJ12 model](https://www.jiecang.com/product/jcb35m11c.html) to test this
-but it's likely that other Jiecang controllers are supported as their serial protocol should be compatible.
-
-## Usage
-
-### What you need
-
-* ESPHome compatible microcontroller
-* depending on your model of desk controller
-    * cable with RJ12 connector (phone cable, RJ11 may also work)
-    * cable with RJ45 connector (network cable)
-
-### Wiring
-
-#### RJ12
-
-Please double check this for your specific model!
-
-pin | function
-----|---------
- 1  | NC (pulled up)
- 2  | GND
- 3  | TX
- 4  | VCC
- 5  | RX
- 6  | NC (pulled up)
-
-#### RJ45
-
-**Untested** and only for reference!
-
-pin | function
-----|---------
- 1  | HS3 [^1]
- 2  | TX
- 3  | GND
- 4  | RX
- 5  | VCC
- 6  | HS2 [^1]
- 7  | HS1 [^1]
- 8  | HS0 [^1]
-
-[^1]: not used here
-
-#### microcontroller
-
-ESP | desk
-----|-----
-GND | GND
-5V  | VCC
-RX  | TX
-TX  | RX
-
-![](https://github.com/Rocka84/esphome_components/assets/2353088/39e08774-a215-4e7e-8345-a2acafce28a2)
-
-## Usage
-
-```yaml
-esphome:
-  ...
-  on_boot:
-    # This script is required to initialize the following sensors:
-    #    height_pct, height_min, height_max, position1 - position4
-    # You can skip this if you don't use those.
-    priority: 0 # when mostly everything else is done
-    then:
-      - lambda: "id(my_desk).request_physical_limits();"
-      - delay: 0.1s
-      - lambda: "id(my_desk).request_limits();"
-      - delay: 0.1s
-      - lambda: "id(my_desk).request_settings();"
-
-external_components:
-  - source:
-      type: git
-      url: https://github.com/Rocka84/esphome_components/
-    components: [ jiecang_desk_controller ]
-
-uart:
-  id: uart_bus
-  tx_pin: TX
-  rx_pin: RX
-  baud_rate: 9600
-
-logger:
-  baud_rate: 0 # disable logging over uart, required when using the RX/TX pins for the controller
-
-jiecang_desk_controller:
-  id: my_desk
-  sensors:
-    height:
-      name: "Height"
-  numbers:
-    height:
-      name: "Height"
-  buttons:
-    move_up:
-      name: "Move up"
-    move_down:
-      name: "Move down"
-    stop:
-      name: "Stop"
-    position1:
-      name: "Position 1"
-
-button:
-  - platform: template
-    name: "Step down"
-    on_press:
-      lambda: "id(my_desk).goto_height(84.0);"
-```
-
-### Examples
-
-* basic, sufficient for most people: [example_basic.yaml](example_basic.yaml)
-* full with all features in use: [example_full.yaml](example_full.yaml)
-
-### Features
-
-#### sensors
-
-sensor     | description
------------|----------------------------
-height     | current height of the desk
-height_pct | height in percent
-height_min | minimal height
-height_max | maximal height
-position1  | 1st stored height
-position2  | 2nd stored height
-position3  | 3rd stored height
-position4  | 4th stored height
-
-####  number entities
-
-Hold current values read from the desk and set values to desk when changed.
-
-sensor     | description
------------|----------------------------
-height     | current height of the desk
-
-
-####  buttons
-
-button        | description
---------------|---------------------------
-step_up       | move up desk by one step (~14mm)
-step_down     | move down desk by one step (~14mm)
-move_up       | move up until stopped
-move_down     | move down until stopped
-stop          | stop movement of desk
-position1     | move to 1st stored height
-position2     | move to 2nd stored height
-position3     | move to 3rd stored height
-position4     | move to 4th stored height
-save_position | press position button afterwards to store current height to that position
-
-#### lambda methods
-
-lambda method                     | description
-----------------------------------|---------------------------
-`id(my_desk).step_up()`           | step_up desk by one step (~14mm)
-`id(my_desk).step_down()`         | step_down desk by one step (~14mm)
-`id(my_desk).move_up()`           | move up
-`id(my_desk).move_down()`         | move down
-`id(my_desk).stop()`              | stop movement of desk
-`id(my_desk).goto_position(pos)`  | move to stored height `pos`
-`id(my_desk).save_position(pos)`  | save current height to position `pos`
-`id(my_desk).goto_height(height)` | move to specified height
-
-## Case
-
-You can find a 3D-printable case in the [stl folder](stl/) or on [onshape](https://cad.onshape.com/documents/9619b6e4e11b26a3e9d82630/w/ca8259951d8b6bb3513992f7/e/2aa1144a31e5b4c252a13681?renderMode=0&uiState=665987de1c1f4a6c5d2d97a4).
-
-## Sources
-
-Thanks to [phord/Jarvis](https://github.com/phord/Jarvis) for reverse engineering the UART interface and most control messages!  
-Thanks to [OkhammahkO](https://github.com/OkhammahkO) for collecting the scattered information at [pimp-my-desk/desk-control](https://gitlab.com/pimp-my-desk/desk-control) and for his work in the home assistant community!
-
diff --git a/components/jiecang_desk_controller/__init__.py b/components/jiecang_desk_controller/__init__.py
deleted file mode 100644
index f0ec16d..0000000
--- a/components/jiecang_desk_controller/__init__.py
+++ /dev/null
@@ -1,174 +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_HEIGHT, CONF_UNIT_OF_MEASUREMENT, CONF_ACCURACY_DECIMALS, UNIT_CENTIMETER, UNIT_PERCENT
-
-DEPENDENCIES = ['uart']
-AUTO_LOAD = ['sensor', 'button', 'number']
-
-jiecang_desk_controller_ns = cg.esphome_ns.namespace('jiecang_desk_controller')
-
-JiecangDeskController = jiecang_desk_controller_ns.class_('JiecangDeskController', cg.Component, uart.UARTDevice)
-JiecangDeskButton = jiecang_desk_controller_ns.class_('JiecangDeskButton', button.Button, cg.Component)
-JiecangDeskNumber = jiecang_desk_controller_ns.class_('JiecangDeskNumber', 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_HEIGHT_MIN = "height_min"
-CONF_HEIGHT_MAX = "height_max"
-CONF_HEIGHT_PCT = "height_pct"
-CONF_POSITION1 = "position1"
-CONF_POSITION2 = "position2"
-CONF_POSITION3 = "position3"
-CONF_POSITION4 = "position4"
-CONF_SAVE_POSITION = "save_position"
-CONF_MOVE_UP = "move_up"
-CONF_MOVE_DOWN = "move_down"
-
-
-button_constants = {}
-button_constants[CONF_STEP_UP] = 0
-button_constants[CONF_STEP_DOWN] = 1
-button_constants[CONF_STOP] = 2
-button_constants[CONF_POSITION1] = 3
-button_constants[CONF_POSITION2] = 4
-button_constants[CONF_POSITION3] = 5
-button_constants[CONF_POSITION4] = 6
-button_constants[CONF_SAVE_POSITION] = 7
-button_constants[CONF_MOVE_UP] = 8
-button_constants[CONF_MOVE_DOWN] = 9
-
-# Backward compatibility
-CONF_RAISE = "raise"
-CONF_LOWER = "lower"
-button_constants[CONF_RAISE] = button_constants[CONF_STEP_UP]
-button_constants[CONF_LOWER] = button_constants[CONF_STEP_DOWN]
-
-
-number_constants = {}
-number_constants[CONF_HEIGHT] = 0
-number_constants[CONF_HEIGHT_PCT] = 1
-
-CONFIG_SCHEMA = cv.COMPONENT_SCHEMA.extend({
-    cv.GenerateID(): cv.declare_id(JiecangDeskController),
-    cv.Optional(CONF_SENSORS): cv.Schema({
-        cv.Optional(CONF_HEIGHT): sensor.sensor_schema(
-            accuracy_decimals = 1,
-            unit_of_measurement = UNIT_CENTIMETER
-        ),
-        cv.Optional(CONF_UNIT): sensor.sensor_schema(
-            accuracy_decimals = 0
-        ),
-        cv.Optional(CONF_HEIGHT_PCT): sensor.sensor_schema(
-            accuracy_decimals = 1,
-            unit_of_measurement = UNIT_PERCENT
-        ),
-        cv.Optional(CONF_HEIGHT_MIN): sensor.sensor_schema(
-            accuracy_decimals = 1,
-            unit_of_measurement = UNIT_CENTIMETER
-        ),
-        cv.Optional(CONF_HEIGHT_MAX): sensor.sensor_schema(
-            accuracy_decimals = 1,
-            unit_of_measurement = UNIT_CENTIMETER
-        ),
-        cv.Optional(CONF_POSITION1): sensor.sensor_schema(
-            accuracy_decimals = 1,
-            unit_of_measurement = UNIT_CENTIMETER
-        ),
-        cv.Optional(CONF_POSITION2): sensor.sensor_schema(
-            accuracy_decimals = 1,
-            unit_of_measurement = UNIT_CENTIMETER
-        ),
-        cv.Optional(CONF_POSITION3): sensor.sensor_schema(
-            accuracy_decimals = 1,
-            unit_of_measurement = UNIT_CENTIMETER
-        ),
-        cv.Optional(CONF_POSITION4): sensor.sensor_schema(
-            accuracy_decimals = 1,
-            unit_of_measurement = UNIT_CENTIMETER
-        ),
-    }),
-    cv.Optional(CONF_NUMBERS): cv.Schema({
-        cv.Optional(CONF_HEIGHT): number.NUMBER_SCHEMA.extend({
-            cv.GenerateID(): cv.declare_id(JiecangDeskNumber),
-            cv.Optional(CONF_ACCURACY_DECIMALS, default=1): cv.int_,
-            cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=UNIT_CENTIMETER): cv.string,
-        }),
-        cv.Optional(CONF_HEIGHT_PCT): number.NUMBER_SCHEMA.extend({
-            cv.GenerateID(): cv.declare_id(JiecangDeskNumber),
-            cv.Optional(CONF_ACCURACY_DECIMALS, default=1): cv.int_,
-            cv.Optional(CONF_UNIT_OF_MEASUREMENT, default=UNIT_PERCENT): cv.string,
-        }),
-    }),
-    cv.Optional(CONF_BUTTONS): cv.Schema({
-        cv.Optional(CONF_STEP_UP): button.BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}),
-        cv.Optional(CONF_STEP_DOWN): button.BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}),
-        cv.Optional(CONF_STOP): button.BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}),
-        cv.Optional(CONF_POSITION1): button.BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}),
-        cv.Optional(CONF_POSITION2): button.BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}),
-        cv.Optional(CONF_POSITION3): button.BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}),
-        cv.Optional(CONF_POSITION4): button.BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}),
-        cv.Optional(CONF_SAVE_POSITION): button.BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}),
-        cv.Optional(CONF_MOVE_UP): button.BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}),
-        cv.Optional(CONF_MOVE_DOWN): button.BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}),
-        # Backward compatibility
-        cv.Optional(CONF_RAISE): button.BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}),
-        cv.Optional(CONF_LOWER): button.BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}),
-    }),
-}).extend(uart.UART_DEVICE_SCHEMA)
-
-async def to_code(config):
-    var = cg.new_Pvariable(config[CONF_ID])
-    await cg.register_component(var, config)
-    await uart.register_uart_device(var, config)
-
-    if CONF_SENSORS in config:
-        sensors = config[CONF_SENSORS]
-
-        if CONF_HEIGHT in sensors:
-            sens = await sensor.new_sensor(sensors[CONF_HEIGHT])
-            cg.add(var.set_sensor_height(sens))
-        if CONF_UNIT in sensors:
-            sens = await sensor.new_sensor(sensors[CONF_UNIT])
-            cg.add(var.set_sensor_unit(sens))
-        if CONF_HEIGHT_MIN in sensors:
-            sens = await sensor.new_sensor(sensors[CONF_HEIGHT_MIN])
-            cg.add(var.set_sensor_height_min(sens))
-        if CONF_HEIGHT_MAX in sensors:
-            sens = await sensor.new_sensor(sensors[CONF_HEIGHT_MAX])
-            cg.add(var.set_sensor_height_max(sens))
-        if CONF_HEIGHT_PCT in sensors:
-            sens = await sensor.new_sensor(sensors[CONF_HEIGHT_PCT])
-            cg.add(var.set_sensor_height_pct(sens))
-        if CONF_POSITION1 in sensors:
-            sens = await sensor.new_sensor(sensors[CONF_POSITION1])
-            cg.add(var.set_sensor_position1(sens))
-        if CONF_POSITION2 in sensors:
-            sens = await sensor.new_sensor(sensors[CONF_POSITION2])
-            cg.add(var.set_sensor_position2(sens))
-        if CONF_POSITION3 in sensors:
-            sens = await sensor.new_sensor(sensors[CONF_POSITION3])
-            cg.add(var.set_sensor_position3(sens))
-        if CONF_POSITION4 in sensors:
-            sens = await sensor.new_sensor(sensors[CONF_POSITION4])
-            cg.add(var.set_sensor_position4(sens))
-
-    if CONF_BUTTONS in config:
-        buttons = config[CONF_BUTTONS]
-        for button_type in buttons.keys():
-            btn = await button.new_button(buttons[button_type])
-            cg.add(var.add_button(btn, button_constants[button_type]))
-
-    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/jiecang_desk_controller/jiecang_desk_controller.cpp b/components/jiecang_desk_controller/jiecang_desk_controller.cpp
deleted file mode 100644
index 225f0d1..0000000
--- a/components/jiecang_desk_controller/jiecang_desk_controller.cpp
+++ /dev/null
@@ -1,314 +0,0 @@
-#include "jiecang_desk_controller.h"
-#include "esphome/core/log.h"
-
-namespace esphome {
-    namespace jiecang_desk_controller {
-
-        static const char *const TAG = "jiecang_desk_controller";
-
-        float JiecangDeskController::byte2float(int high, int low) {
-            return static_cast<float>((high<<8) + low)/10;
-        }
-
-        bool JiecangDeskController::bufferMessage(int data, unsigned int *buffer, int len) {
-            // This is a very simple method of receiving messages from the desk.
-            // It will fail on messages that contain the value 0x7E in their payload.
-            // But it is super simple and works for the messages we care about.
-
-            // format: 0xF2 0xF2 [command] [param_count] [[param] ...] [checksum] 0x7E
-            // checksum: sum of [command], [param_count] and all [param]s
-
-            static int cmd_incoming = 0; // 0: wait for F2, 1: wait for 2nd F2, 2: buffer data
-            static int pos = 0;
-
-            if (cmd_incoming < 2 && data == 0xF2) { // start of message, must appear twice
-                cmd_incoming++;
-                pos = 0;
-
-            } else if (cmd_incoming == 1) { // no second F2 received
-                cmd_incoming = 0;
-
-            } else if (cmd_incoming == 2) {
-                if (data == 0x7E) { // end of message
-                    cmd_incoming = 0;
-                    for (;pos<len-1;pos++) { // fill rest of buffer with zeros
-                        buffer[pos]=0;
-                    }
-                    return true;
-
-                } else if (pos >= len) { // message too long, drop it
-                    cmd_incoming = 0;
-
-                } else {
-                    buffer[pos++] = data; // buffer data
-                }
-
-            } // else: received garbage
-
-            return false;
-        }
-
-        void JiecangDeskController::handleMessage(unsigned int *message) {
-            // ESP_LOGV("jiecang_desk_controller", "message %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", message[0], message[1], message[2], message[3], message[4], message[5], message[6], message[7], message[8], message[9]);
-
-            switch (message[0]) {
-                case 0x01:
-                    ESP_LOGV("jiecang_desk_controller", "height 0x%0X%0X", message[2], message[3]);
-                    float new_height;
-                    new_height = byte2float(message[2], message[3]);
-                    if (new_height == current_height) return;
-                    current_height = new_height;
-                    if (height != nullptr) height->publish_state(current_height);
-                    if (number_height != nullptr) number_height->publish_state(current_height);
-
-                    if (height_pct != nullptr && limit_max != 0)
-                        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("jiecang_desk_controller", "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 (height_max != nullptr) height_max->publish_state(limit_max);
-                        if (number_height != nullptr) number_height->set_max_value(limit_max);
-                    }
-                    if ((message[2]>>4) == 0) { // high nibble 0 -> no min limit, use physical_min
-                        limit_min = physical_min;
-                        if (height_min != nullptr) height_min->publish_state(limit_min);
-                        if (number_height != nullptr) number_height->set_min_value(limit_min);
-                    }
-                    break;
-
-                case 0x07:
-                    ESP_LOGV("jiecang_desk_controller", "physical limits 0x%02X%02X 0x%02X%02X", message[2], message[3], message[4], message[5]);
-                    physical_max = byte2float(message[2], message[3]);
-                    physical_min = byte2float(message[4], message[5]);
-                    break;
-
-                case 0x21:
-                    ESP_LOGV("jiecang_desk_controller", "height_max 0x%02X%02X", message[2], message[3]);
-                    limit_max = byte2float(message[2], message[3]);
-                    if (height_max != nullptr) height_max->publish_state(limit_max);
-                    if (number_height != nullptr) number_height->set_max_value(limit_max);
-                    break;
-
-                case 0x22:
-                    ESP_LOGV("jiecang_desk_controller", "height_min 0x%02X%02X", message[2], message[3]);
-                    limit_min = byte2float(message[2], message[3]);
-                    if (height_min != nullptr) height_min->publish_state(limit_min);
-                    if (number_height != nullptr) number_height->set_min_value(limit_min);
-                    break;
-
-                case 0x25:
-                    ESP_LOGV("jiecang_desk_controller", "position1 0x%02X%02X", message[2], message[3]);
-                    if (position1 != nullptr) position1->publish_state(byte2float(message[2], message[3]));
-                    break;
-
-                case 0x26:
-                    ESP_LOGV("jiecang_desk_controller", "position2 0x%02X%02X", message[2], message[3]);
-                    if (position2 != nullptr) position2->publish_state(byte2float(message[2], message[3]));
-                    break;
-
-                case 0x27:
-                    ESP_LOGV("jiecang_desk_controller", "position3 0x%02X%02X", message[2], message[3]);
-                    if (position3 != nullptr) position3->publish_state(byte2float(message[2], message[3]));
-                    break;
-
-                case 0x28:
-                    ESP_LOGV("jiecang_desk_controller", "position4 0x%02X%02X", message[2], message[3]);
-                    if (position4 != nullptr) position4->publish_state(byte2float(message[2], message[3]));
-                    break;
-
-                default:
-                    ESP_LOGV("jiecang_desk_controller", "unknown message %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X", message[0], message[1], message[2], message[3], message[4], message[5], message[6], message[7], message[8], message[9]);
-            }
-        }
-
-        void JiecangDeskController::update() {
-            const int max_length = 10;
-            static unsigned int buffer[max_length];
-            while (available()) {
-                if(bufferMessage(read(), buffer, max_length)) {
-                    handleMessage(buffer);
-                }
-            }
-        }
-
-        void JiecangDeskController::send_simple_command(unsigned char cmd) {
-            write_array({ 0xF1, 0xF1, cmd, 0x00, cmd, 0x7E });
-        }
-
-        void JiecangDeskController::add_button(button::Button *btn, int action) {
-            btn->add_on_press_callback([this, action]() { this->button_press_action(action); });
-        }
-
-        void JiecangDeskController::step_up() {
-            send_simple_command(0x01);
-        }
-
-        void JiecangDeskController::step_down() {
-            send_simple_command(0x02);
-        }
-
-        void JiecangDeskController::stop() {
-            send_simple_command(0x2B);
-        }
-
-        void JiecangDeskController::goto_position(int pos) {
-            switch (pos) {
-                case 1:
-                    send_simple_command(0x05);
-                    break;
-                case 2:
-                    send_simple_command(0x06);
-                    break;
-                case 3:
-                    send_simple_command(0x27);
-                    break;
-                case 4:
-                    send_simple_command(0x28);
-                    break;
-            }
-        }
-
-        void JiecangDeskController::save_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 JiecangDeskController::goto_height(float height) {
-            unsigned char cmd = 0x1B;
-            unsigned char high_byte = ((int) height * 10) >> 8;
-            unsigned char low_byte = ((int) height * 10) & 0xFF;
-
-            unsigned char checksum = cmd + 2 + high_byte + low_byte;
-
-            write_array({ 0xF1, 0xF1, cmd, 0x02, high_byte, low_byte, checksum, 0x7E });
-        }
-
-        void JiecangDeskController::move_up() {
-            goto_height(limit_max);
-        }
-
-        void JiecangDeskController::move_down() {
-            goto_height(limit_min);
-        }
-
-        void JiecangDeskController::request_physical_limits() {
-            send_simple_command(0x0C);
-        }
-
-        void JiecangDeskController::request_limits() {
-            send_simple_command(0x20);
-        }
-
-        void JiecangDeskController::request_settings() {
-            send_simple_command(0x07);
-        }
-
-        void JiecangDeskController::button_press_action(int action) {
-            ESP_LOGV("JiecangDeskController", "button_press_action %i", action);
-            switch (action) {
-                case BUTTON_STEP_UP:
-                    step_up();
-                    break;
-                case BUTTON_STEP_DOWN:
-                    step_down();
-                    break;
-                case BUTTON_STOP:
-                    stop();
-                    save_position_mode = false;
-                    break;
-                case BUTTON_MOVE_UP:
-                    move_up();
-                    break;
-                case BUTTON_MOVE_DOWN:
-                    move_down();
-                    break;
-                case BUTTON_POSITION1:
-                    if (save_position_mode) {
-                        save_position_mode = false;
-                        save_position(1);
-                        return;
-                    }
-                    goto_position(1);
-                    break;
-                case BUTTON_POSITION2:
-                    if (save_position_mode) {
-                        save_position_mode = false;
-                        save_position(2);
-                        return;
-                    }
-                    goto_position(2);
-                    break;
-                case BUTTON_POSITION3:
-                    if (save_position_mode) {
-                        save_position_mode = false;
-                        save_position(3);
-                        return;
-                    }
-                    goto_position(3);
-                    break;
-                case BUTTON_POSITION4:
-                    if (save_position_mode) {
-                        save_position_mode = false;
-                        save_position(4);
-                        return;
-                    }
-                    goto_position(4);
-                    break;
-                case BUTTON_SAVE_POSITION:
-                    save_position_mode = true;
-                    break;
-            }
-        }
-
-        void JiecangDeskController::add_number(JiecangDeskNumber *number, int type) {
-            switch (type) {
-                case NUMBER_HEIGHT:
-                    number_height = 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 JiecangDeskController::number_control(int type, float value) {
-            ESP_LOGV("JiecangDeskController", "number_control %i", type);
-            switch (type) {
-                case NUMBER_HEIGHT:
-                    goto_height(value);
-                    break;
-                case NUMBER_HEIGHT_PCT:
-                    if (limit_max != 0) goto_height((limit_max - limit_min) * value / 100 + limit_min);
-                    break;
-            }
-        }
-
-        void JiecangDeskButton::press_action() {}
-
-    } //namespace jiecang_desk_controller
-} //namespace esphome
-
diff --git a/components/jiecang_desk_controller/jiecang_desk_controller.h b/components/jiecang_desk_controller/jiecang_desk_controller.h
deleted file mode 100644
index 4f0a6ef..0000000
--- a/components/jiecang_desk_controller/jiecang_desk_controller.h
+++ /dev/null
@@ -1,98 +0,0 @@
-#pragma once
-
-#include <future>
-#include "esphome/core/component.h"
-#include "esphome/components/sensor/sensor.h"
-#include "esphome/components/button/button.h"
-#include "esphome/components/uart/uart.h"
-#include "esphome/core/hal.h"
-#include "esphome/core/log.h"
-#include "jiecang_desk_number.h"
-
-#define BUTTON_STEP_UP 0
-#define BUTTON_STEP_DOWN 1
-#define BUTTON_STOP 2
-#define BUTTON_POSITION1 3
-#define BUTTON_POSITION2 4
-#define BUTTON_POSITION3 5
-#define BUTTON_POSITION4 6
-#define BUTTON_SAVE_POSITION 7
-#define BUTTON_MOVE_UP 8
-#define BUTTON_MOVE_DOWN 9
-
-#define NUMBER_HEIGHT 0
-#define NUMBER_HEIGHT_PCT 1
-
-namespace esphome {
-    namespace jiecang_desk_controller {
-
-        class JiecangDeskController : 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;
-
-                bool save_position_mode = false;
-
-                float byte2float(int high, int low);
-                bool bufferMessage(int data, unsigned int *buffer, int len);
-                void handleMessage(unsigned int *message);
-
-            public:
-                void update() override;
-
-                void set_sensor_height(sensor::Sensor *sensor) { this->height = sensor; }
-                void set_sensor_height_min(sensor::Sensor *sensor) { this->height_min = sensor; }
-                void set_sensor_height_max(sensor::Sensor *sensor) { this->height_max = sensor; }
-                void set_sensor_height_pct(sensor::Sensor *sensor) { this->height_pct = sensor; }
-
-                void set_sensor_position1(sensor::Sensor *sensor) { this->position1 = sensor; }
-                void set_sensor_position2(sensor::Sensor *sensor) { this->position2 = sensor; }
-                void set_sensor_position3(sensor::Sensor *sensor) { this->position3 = sensor; }
-                void set_sensor_position4(sensor::Sensor *sensor) { this->position4 = sensor; }
-
-                void send_simple_command(unsigned char cmd);
-                void add_button(button::Button *btn, int action);
-                void add_number(JiecangDeskNumber *number, int type);
-
-                void step_up();
-                void step_down();
-                void stop();
-                void goto_position(int pos);
-                void save_position(int pos);
-                void goto_height(float height);
-                void move_up();
-                void move_down();
-
-                void request_physical_limits();
-                void request_limits();
-                void request_settings();
-
-                void number_control(int type, float value);
-
-            protected:
-                Sensor *height{nullptr};
-                Sensor *height_min{nullptr};
-                Sensor *height_max{nullptr};
-                Sensor *height_pct{nullptr};
-
-                Sensor *position1{nullptr};
-                Sensor *position2{nullptr};
-                Sensor *position3{nullptr};
-                Sensor *position4{nullptr};
-
-                JiecangDeskNumber *number_height{nullptr};
-                JiecangDeskNumber *number_height_pct{nullptr};
-
-                void button_press_action(int type);
-        };
-
-        class JiecangDeskButton : public Component, public button::Button {
-            protected:
-                void press_action() override;
-        };
-
-    } //namespace jiecang_desk_controller
-} //namespace esphome
diff --git a/components/jiecang_desk_controller/jiecang_desk_number.cpp b/components/jiecang_desk_controller/jiecang_desk_number.cpp
deleted file mode 100644
index de15d91..0000000
--- a/components/jiecang_desk_controller/jiecang_desk_number.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-#include "jiecang_desk_number.h"
-#include "jiecang_desk_controller.h"
-#include "esphome/core/log.h"
-
-namespace esphome {
-    namespace jiecang_desk_controller  {
-
-        static const char *const TAG = "jiecang_desk.number";
-
-        void JiecangDeskNumber::setup() {}
-
-        void JiecangDeskNumber::dump_config() {
-            LOG_NUMBER("", "JiecangDeskNumber", this);
-            ESP_LOGCONFIG(TAG, " type %i", type);
-        }
-
-        void JiecangDeskNumber::control(float value) {
-            this->publish_state(value);
-            parent->number_control(this->type, value);
-        }
-
-        void JiecangDeskNumber::set_min_value(float value) {
-            traits.set_min_value(value);
-        }
-
-        void JiecangDeskNumber::set_max_value(float value) {
-            traits.set_max_value(value);
-        }
-
-
-    }  // namespace jiecang_desk_controller
-}  // namespace esphome
diff --git a/components/jiecang_desk_controller/jiecang_desk_number.h b/components/jiecang_desk_controller/jiecang_desk_number.h
deleted file mode 100644
index 72e0a5d..0000000
--- a/components/jiecang_desk_controller/jiecang_desk_number.h
+++ /dev/null
@@ -1,33 +0,0 @@
-#pragma once
-
-#include "esphome/core/component.h"
-#include "esphome/components/number/number.h"
-#include "esphome/core/log.h"
-
-namespace esphome {
-    namespace jiecang_desk_controller {
-
-        class JiecangDeskController;
-
-        class JiecangDeskNumber : public number::Number, public Component {
-            private:
-                int type;
-                JiecangDeskController *parent;
-
-            public:
-                void setup() override;
-                void dump_config() override;
-
-                void set_parent(JiecangDeskController *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 jiecang_desk_controller
-}  // namespace esphome
diff --git a/components/maidsite_desk_controller/README.md b/components/maidsite_desk_controller/README.md
new file mode 100644
index 0000000..b3850b9
--- /dev/null
+++ b/components/maidsite_desk_controller/README.md
@@ -0,0 +1,198 @@
+# ESPHome Maidsite Desk Controller
+
+[ESPHome](https://esphome.io/) component for controlling Maidsite desk controllers via their serial protocol.
+
+![](https://github.com/Rocka84/esphome_components/assets/2353088/b04d1fd7-d0ed-443d-87b7-87454757c0f0)
+
+Attention: I only have one [RJ12 model](https://www.maidsite.com/product/jcb35m11c.html) to test this
+but it's likely that other Maidsite controllers are supported as their serial protocol should be compatible.
+
+## Usage
+
+### What you need
+
+* ESPHome compatible microcontroller
+* depending on your model of desk controller
+    * cable with RJ12 connector (phone cable, RJ11 may also work)
+    * cable with RJ45 connector (network cable)
+
+### Wiring
+
+#### RJ12
+
+Please double check this for your specific model!
+
+pin | function
+----|---------
+ 1  | NC (pulled up)
+ 2  | GND
+ 3  | TX
+ 4  | VCC
+ 5  | RX
+ 6  | NC (pulled up)
+
+#### RJ45
+
+**Untested** and only for reference!
+
+pin | function
+----|---------
+ 1  | HS3 [^1]
+ 2  | TX
+ 3  | GND
+ 4  | RX
+ 5  | VCC
+ 6  | HS2 [^1]
+ 7  | HS1 [^1]
+ 8  | HS0 [^1]
+
+[^1]: not used here
+
+#### microcontroller
+
+ESP | desk
+----|-----
+GND | GND
+5V  | VCC
+RX  | TX
+TX  | RX
+
+![](https://github.com/Rocka84/esphome_components/assets/2353088/39e08774-a215-4e7e-8345-a2acafce28a2)
+
+## Usage
+
+```yaml
+esphome:
+  ...
+  on_boot:
+    # This script is required to initialize the following sensors:
+    #    height_pct, height_min, height_max, position_m1 - position_m4
+    # You can skip this if you don't use those.
+    priority: 0 # when mostly everything else is done
+    then:
+      - lambda: "id(my_desk).request_physical_limits();"
+      - delay: 0.1s
+      - lambda: "id(my_desk).request_limits();"
+      - delay: 0.1s
+      - lambda: "id(my_desk).request_settings();"
+
+external_components:
+  - source:
+      type: git
+      url: https://github.com/ElVit/esphome_maidsite_desk/
+    components: [ maidsite_desk_controller ]
+
+uart:
+  - id: uart_bus
+    tx_pin: $pin_tx
+    rx_pin: $pin_rx
+    baud_rate: 9600
+
+logger:
+  baud_rate: 0 # disable logging over uart, required when using the RX/TX pins for the controller
+
+maidsite_desk_controller:
+  id: my_desk
+
+  sensors:
+    height_abs:
+      name: "Height"
+
+  numbers:
+    height_abs:
+      name: "Height"
+      mode: SLIDER
+
+  buttons:
+    stop:
+      name: "Stop"
+    step_up:
+      name: "Step up"
+    step_down:
+      name: "Step down"
+    goto_m1:
+      name: "Move to M1"
+    goto_m2:
+      name: "Move to M2"
+    goto_m3:
+      name: "Move to M3"
+    goto_m4:
+      name: "Move to M4"
+
+button:
+  - platform: template
+    name: "Step down"
+    on_press:
+      lambda: "id(my_desk).goto_height(84.0);"
+```
+
+### Examples
+
+* basic, sufficient for most people: [example_basic.yaml](example_basic.yaml)
+* full with all features in use: [example_full.yaml](example_full.yaml)
+
+### Features
+
+#### sensors
+
+sensor       | description
+-------------|----------------------------
+height_abs   | current height of the desk
+height_pct   | height in percent
+height_min   | minimal height
+height_max   | maximal height
+position_m1  | 1st stored height
+position_m2  | 2nd stored height
+position_m3  | 3rd stored height
+position_m4  | 4th stored height
+
+####  number entities
+
+Hold current values read from the desk and set values to desk when changed.
+
+sensor      | description
+------------|----------------------------
+height_abs  | current height of the desk
+height_pct  | height in percent
+
+####  buttons
+
+button     | description
+-----------|---------------------------
+step_up    | move up desk by one step (~14mm)
+step_down  | move down desk by one step (~14mm)
+goto_max   | move up until stopped
+goto_min   | move down until stopped
+stop       | stop movement of desk
+goto_m1    | move to 1st stored height
+goto_m2    | move to 2nd stored height
+goto_m3    | move to 3rd stored height
+goto_m4    | move to 4th stored height
+save_m1    | save current height to 1th position
+save_m2    | save current height to 2th position
+save_m3    | save current height to 3th position
+save_m4    | save current height to 4th position
+
+#### lambda methods
+
+lambda method                         | description
+--------------------------------------|---------------------------
+`id(my_desk).step_up()`               | step_up desk by one step (~14mm)
+`id(my_desk).step_down()`             | step_down desk by one step (~14mm)
+`id(my_desk).goto_max_position()`     | move up
+`id(my_desk).goto_min_position()`     | move down
+`id(my_desk).stop()`                  | stop movement of desk
+`id(my_desk).goto_mem_position(pos)`  | move to stored height `pos`
+`id(my_desk).save_mem_position(pos)`  | save current height to position `pos`
+`id(my_desk).goto_height(height)`     | move to specified height
+
+## Case
+
+You can find a 3D-printable case in the [stl folder](stl/) or on [onshape](https://cad.onshape.com/documents/9619b6e4e11b26a3e9d82630/w/ca8259951d8b6bb3513992f7/e/2aa1144a31e5b4c252a13681?renderMode=0&uiState=665987de1c1f4a6c5d2d97a4).
+
+## Sources
+
+Thanks to [Rocka84](https://github.com/Rocka84/esphome_components)
+Thanks to [phord/Jarvis](https://github.com/phord/Jarvis) for reverse engineering the UART interface and most control messages!  
+Thanks to [OkhammahkO](https://github.com/OkhammahkO) for collecting the scattered information at [pimp-my-desk/desk-control](https://gitlab.com/pimp-my-desk/desk-control) and for his work in the home assistant community!
+
diff --git a/components/maidsite_desk_controller/__init__.py b/components/maidsite_desk_controller/__init__.py
new file mode 100644
index 0000000..43d9e42
--- /dev/null
+++ b/components/maidsite_desk_controller/__init__.py
@@ -0,0 +1,184 @@
+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/jiecang_desk_controller/example_basic.yaml b/components/maidsite_desk_controller/example_basic.yaml
similarity index 57%
rename from components/jiecang_desk_controller/example_basic.yaml
rename to components/maidsite_desk_controller/example_basic.yaml
index dc44217..1303ef0 100644
--- a/components/jiecang_desk_controller/example_basic.yaml
+++ b/components/maidsite_desk_controller/example_basic.yaml
@@ -1,6 +1,6 @@
 esphome:
-  name: jiecang-desk-controller # change this!
-  friendly_name: Jiecang Desk Controller # change this!
+  name: maidsite-desk-controller # change this!
+  friendly_name: Maidsite Desk Controller # change this!
   on_boot:
     # don't touch if you don't know what you're doing!
     priority: 0 # when mostly everything else is done
@@ -14,43 +14,47 @@ esphome:
 external_components:
   - source:
       type: git
-      url: https://github.com/Rocka84/esphome_components/
-    components: [ jiecang_desk_controller ]
+      url: https://github.com/ElVit/esphome_maidsite_desk/
+    components: [ maidsite_desk_controller ]
 
 uart:
-  id: uart_bus
-  tx_pin: TX
-  rx_pin: RX
-  baud_rate: 9600
+  - id: uart_bus
+    tx_pin: $pin_tx
+    rx_pin: $pin_rx
+    baud_rate: 9600
 
-logger:
-  baud_rate: 0 # disable logging over uart, required when using the RX/TX pins for the controller
-
-# see full example for more options: https://github.com/Rocka84/esphome_components/blob/master/components/jiecang_desk_controller/example_full.yaml
-jiecang_desk_controller:
+# see full example for more options: https://github.com/Rocka84/esphome_components/blob/master/components/maidsite_desk_controller/example_full.yaml
+maidsite_desk_controller:
   id: my_desk
+
+  sensors:
+    height_abs:
+      name: "Height"
+
   buttons:
-    move_up:
-      name: "Move up"
-    move_down:
-      name: "Move down"
     stop:
       name: "Stop"
     step_up:
       name: "Step up"
     step_down:
       name: "Step down"
-    position1:
-      name: "Position 1"
-    position2:
-      name: "Position 2"
-    position3:
-      name: "Position 3"
-    position4:
-      name: "Position 4"
+    goto_max:
+      name: "Move to MAX"
+    goto_min:
+      name: "Move to MIN"
+    goto_m1:
+      name: "Move to M1"
+    goto_m2:
+      name: "Move to M2"
+    goto_m3:
+      name: "Move to M3"
+    goto_m4:
+      name: "Move to M4"
+
   numbers:
-    height:
+    height_abs:
       name: "Height"
+      mode: SLIDER
 
 # the usual stuff
 
@@ -70,3 +74,4 @@ api:
     key: !secret encryption_key
 ota:
 
+logger:
diff --git a/components/jiecang_desk_controller/example_full.yaml b/components/maidsite_desk_controller/example_full.yaml
similarity index 54%
rename from components/jiecang_desk_controller/example_full.yaml
rename to components/maidsite_desk_controller/example_full.yaml
index 3647a94..7407eb7 100644
--- a/components/jiecang_desk_controller/example_full.yaml
+++ b/components/maidsite_desk_controller/example_full.yaml
@@ -1,9 +1,9 @@
 esphome:
-  name: jiecang-desk-controller
-  friendly_name: Jiecang Desk Controller
+  name: maidsite-desk-controller
+  friendly_name: Maidsite Desk Controller
   on_boot:
     # This script is required to initialize the following sensors:
-    #    height_pct, height_min, height_max, position1 - position4
+    #    height_pct, height_min, height_max, position_m1 - position_m4
     # You can skip this if you don't use those.
     priority: 0 # when mostly everything else is done
     then:
@@ -16,69 +16,79 @@ esphome:
 external_components:
   - source:
       type: local
-      path: rocka84_esphome_components/components/
+      path: elvit_esphome_maidsite_desk/components/
       # type: git
-      # url: https://github.com/Rocka84/esphome_components/
-    components: [ jiecang_desk_controller ]
+      # url: https://github.com/ElVit/esphome_maidsite_desk/
+    components: [ maidsite_desk_controller ]
 
 uart:
-  id: uart_bus
-  tx_pin: TX
-  rx_pin: RX
-  baud_rate: 9600
+  - id: uart_bus
+    tx_pin: $pin_tx
+    rx_pin: $pin_rx
+    baud_rate: 9600
+    # debug:
+    #   direction: BOTH
+    #   after:
+    #     bytes: 9
+    #   sequence:
+    #     - lambda: UARTDebug::log_hex(direction, bytes, ',');
 
-logger:
-  baud_rate: 0 # disable logging over uart, required when using the RX/TX pins for the controller
-
-jiecang_desk_controller:
+maidsite_desk_controller:
   id: my_desk
+
   sensors:
-    height:
-      name: "Height"
+    height_abs:
+      name: "Height absolut"
+    height_pct:
+      name: "Height percent"
     height_min:
       name: "Height Min"
     height_max:
       name: "Height Max"
-    height_pct:
-      name: "Height Percent"
-    position1:
-      name: "Position 1"
-    position2:
-      name: "Position 2"
-    position3:
-      name: "Position 3"
-    position4:
-      name: "Position 4"
+    position_m1:
+      name: "Position M1"
+    position_m2:
+      name: "Position M2"
+    position_m3:
+      name: "Position M3"
+    position_m4:
+      name: "Position M4"
+
   buttons:
-    move_up:
-      name: "Move up"
-    move_down:
-      name: "Move down"
     stop:
       name: "Stop"
     step_up:
       name: "Step up"
     step_down:
       name: "Step down"
-    position1:
-      name: "Position 1"
-    position2:
-      name: "Position 2"
-    position3:
-      name: "Position 3"
-    position4:
-      name: "Position 4"
-    save_position:
-      name: "Save Position"
-    move_up:
-      name: "Move up"
-    move_down:
-      name: "Move down"
+    goto_max:
+      name: "Move to MAX"
+    goto_min:
+      name: "Move to MIN"
+    goto_m1:
+      name: "Move to M1"
+    goto_m2:
+      name: "Move to M2"
+    goto_m3:
+      name: "Move to M3"
+    goto_m4:
+      name: "Move to M4"
+    save_m1:
+      name: "Save M1"
+    save_m2:
+      name: "Save M2"
+    save_m3:
+      name: "Save M3"
+    save_m4:
+      name: "Save M4"
+
   numbers:
-    height:
-      name: "Height"
+    height_abs:
+      name: "Height absolut"
+      mode: SLIDER
     height_pct:
-      name: "Height Percent"
+      name: "Height percent"
+      mode: SLIDER
 
 ## lambda usage
 # button:
@@ -97,17 +107,16 @@ jiecang_desk_controller:
 #   - platform: template
 #     name: "Position 2"
 #     on_press:
-#       lambda: "id(my_desk).goto_position(2);"
+#       lambda: "id(my_desk).goto_mem_position(2);"
 #   - platform: template
 #     name: "Save Position 4"
 #     on_press:
-#       lambda: "id(my_desk).save_position(4);"
+#       lambda: "id(my_desk).save_mem_position(4);"
 #   - platform: template
 #     name: "Go to 100cm"
 #     on_press:
 #       lambda: "id(my_desk).goto_height(100);"
 
-
 # the usual stuff
 
 esp8266:
@@ -126,3 +135,4 @@ api:
     key: !secret encryption_key
 ota:
 
+logger:
diff --git a/components/maidsite_desk_controller/maidsite_desk_controller.cpp b/components/maidsite_desk_controller/maidsite_desk_controller.cpp
new file mode 100644
index 0000000..5c85b14
--- /dev/null
+++ b/components/maidsite_desk_controller/maidsite_desk_controller.cpp
@@ -0,0 +1,381 @@
+#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<float>((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);
+        }
+      }
+    }
+
+    void MaidsiteDeskController::add_button(button::Button *btn, int action)
+    {
+      btn->add_on_press_callback([this, action]() { this->button_press_action(action); });
+    }
+
+    // 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::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
new file mode 100644
index 0000000..237b129
--- /dev/null
+++ b/components/maidsite_desk_controller/maidsite_desk_controller.h
@@ -0,0 +1,105 @@
+#pragma once
+
+#include <future>
+#include "esphome/core/component.h"
+#include "esphome/components/sensor/sensor.h"
+#include "esphome/components/button/button.h"
+#include "esphome/components/uart/uart.h"
+#include "esphome/core/hal.h"
+#include "esphome/core/log.h"
+#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
new file mode 100644
index 0000000..00a3b49
--- /dev/null
+++ b/components/maidsite_desk_controller/maidsite_desk_number.cpp
@@ -0,0 +1,37 @@
+#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
new file mode 100644
index 0000000..c537f3d
--- /dev/null
+++ b/components/maidsite_desk_controller/maidsite_desk_number.h
@@ -0,0 +1,35 @@
+#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
diff --git a/components/jiecang_desk_controller/stl/D1mini Case Bottom.stl b/components/maidsite_desk_controller/stl/D1mini Case Bottom.stl
similarity index 100%
rename from components/jiecang_desk_controller/stl/D1mini Case Bottom.stl
rename to components/maidsite_desk_controller/stl/D1mini Case Bottom.stl
diff --git a/components/jiecang_desk_controller/stl/D1mini Case Clamp.stl b/components/maidsite_desk_controller/stl/D1mini Case Clamp.stl
similarity index 100%
rename from components/jiecang_desk_controller/stl/D1mini Case Clamp.stl
rename to components/maidsite_desk_controller/stl/D1mini Case Clamp.stl
diff --git a/components/jiecang_desk_controller/stl/D1mini Case Table Clamp A.stl b/components/maidsite_desk_controller/stl/D1mini Case Table Clamp A.stl
similarity index 100%
rename from components/jiecang_desk_controller/stl/D1mini Case Table Clamp A.stl
rename to components/maidsite_desk_controller/stl/D1mini Case Table Clamp A.stl
diff --git a/components/jiecang_desk_controller/stl/D1mini Case Table Clamp B.stl b/components/maidsite_desk_controller/stl/D1mini Case Table Clamp B.stl
similarity index 100%
rename from components/jiecang_desk_controller/stl/D1mini Case Table Clamp B.stl
rename to components/maidsite_desk_controller/stl/D1mini Case Table Clamp B.stl
diff --git a/components/jiecang_desk_controller/stl/D1mini Case Top.stl b/components/maidsite_desk_controller/stl/D1mini Case Top.stl
similarity index 100%
rename from components/jiecang_desk_controller/stl/D1mini Case Top.stl
rename to components/maidsite_desk_controller/stl/D1mini Case Top.stl
diff --git a/example_jiecang_desk_controller.yaml b/example_jiecang_desk_controller.yaml
deleted file mode 120000
index fa6cf37..0000000
--- a/example_jiecang_desk_controller.yaml
+++ /dev/null
@@ -1 +0,0 @@
-components/jiecang_desk_controller/example_basic.yaml
\ No newline at end of file
diff --git a/example_maidsite_desk_controller.yaml b/example_maidsite_desk_controller.yaml
new file mode 100644
index 0000000..ee82bf4
--- /dev/null
+++ b/example_maidsite_desk_controller.yaml
@@ -0,0 +1 @@
+components/maidsite_desk_controller/example_basic.yaml
\ No newline at end of file
