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 ) # ------------------------------------------------------------------- # Robust schema compatibility (ESPHome 2025.11 ↔ 2025.12+) # ------------------------------------------------------------------- BUTTON_SCHEMA = getattr(button, "_BUTTON_SCHEMA", button.BUTTON_SCHEMA) NUMBER_SCHEMA = getattr(number, "_NUMBER_SCHEMA", number.NUMBER_SCHEMA) # ------------------------------------------------------------------- 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 = { CONF_STEP_UP: 0, CONF_STEP_DOWN: 1, CONF_STOP: 2, CONF_POSITION1: 3, CONF_POSITION2: 4, CONF_POSITION3: 5, CONF_POSITION4: 6, CONF_SAVE_POSITION: 7, CONF_MOVE_UP: 8, 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 = { CONF_HEIGHT: 0, 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_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_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_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}), cv.Optional(CONF_STEP_DOWN): BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}), cv.Optional(CONF_STOP): BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}), cv.Optional(CONF_POSITION1): BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}), cv.Optional(CONF_POSITION2): BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}), cv.Optional(CONF_POSITION3): BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}), cv.Optional(CONF_POSITION4): BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}), cv.Optional(CONF_SAVE_POSITION): BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}), cv.Optional(CONF_MOVE_UP): BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}), cv.Optional(CONF_MOVE_DOWN): BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}), # Backward compatibility cv.Optional(CONF_RAISE): BUTTON_SCHEMA.extend({cv.GenerateID(): cv.declare_id(JiecangDeskButton)}), cv.Optional(CONF_LOWER): 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: cg.add(var.set_sensor_height(await sensor.new_sensor(sensors[CONF_HEIGHT]))) if CONF_UNIT in sensors: cg.add(var.set_sensor_unit(await sensor.new_sensor(sensors[CONF_UNIT]))) if CONF_HEIGHT_MIN in sensors: cg.add(var.set_sensor_height_min(await sensor.new_sensor(sensors[CONF_HEIGHT_MIN]))) if CONF_HEIGHT_MAX in sensors: cg.add(var.set_sensor_height_max(await sensor.new_sensor(sensors[CONF_HEIGHT_MAX]))) if CONF_HEIGHT_PCT in sensors: cg.add(var.set_sensor_height_pct(await sensor.new_sensor(sensors[CONF_HEIGHT_PCT]))) if CONF_POSITION1 in sensors: cg.add(var.set_sensor_position1(await sensor.new_sensor(sensors[CONF_POSITION1]))) if CONF_POSITION2 in sensors: cg.add(var.set_sensor_position2(await sensor.new_sensor(sensors[CONF_POSITION2]))) if CONF_POSITION3 in sensors: cg.add(var.set_sensor_position3(await sensor.new_sensor(sensors[CONF_POSITION3]))) if CONF_POSITION4 in sensors: cg.add(var.set_sensor_position4(await sensor.new_sensor(sensors[CONF_POSITION4]))) if CONF_BUTTONS in config: for button_type, button_cfg in config[CONF_BUTTONS].items(): btn = await button.new_button(button_cfg) cg.add(var.add_button(btn, button_constants[button_type])) if CONF_NUMBERS in config: for number_type, number_cfg in config[CONF_NUMBERS].items(): num = await number.new_number( number_cfg, min_value=0, max_value=100, step=0.1, ) cg.add(var.add_number(num, number_constants[number_type]))