From 6ec9cfb0447a4e140e2071fe4947e5f43bf68f0a Mon Sep 17 00:00:00 2001 From: Frank Langtind Date: Mon, 20 Dec 2021 02:35:10 +0100 Subject: [PATCH] Add Tuya Number support (#2765) --- CODEOWNERS | 1 + esphome/components/tuya/number/__init__.py | 54 +++++++++++++++++++ .../components/tuya/number/tuya_number.cpp | 38 +++++++++++++ esphome/components/tuya/number/tuya_number.h | 27 ++++++++++ esphome/const.py | 1 + tests/test4.yaml | 8 ++- 6 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 esphome/components/tuya/number/__init__.py create mode 100644 esphome/components/tuya/number/tuya_number.cpp create mode 100644 esphome/components/tuya/number/tuya_number.h diff --git a/CODEOWNERS b/CODEOWNERS index 2f2260e0e..fed6815a6 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -180,6 +180,7 @@ esphome/components/toshiba/* @kbx81 esphome/components/tsl2591/* @wjcarpenter esphome/components/tuya/binary_sensor/* @jesserockz esphome/components/tuya/climate/* @jesserockz +esphome/components/tuya/number/* @frankiboy1 esphome/components/tuya/sensor/* @jesserockz esphome/components/tuya/switch/* @jesserockz esphome/components/tuya/text_sensor/* @dentra diff --git a/esphome/components/tuya/number/__init__.py b/esphome/components/tuya/number/__init__.py new file mode 100644 index 000000000..12c0c0f6e --- /dev/null +++ b/esphome/components/tuya/number/__init__.py @@ -0,0 +1,54 @@ +from esphome.components import number +import esphome.config_validation as cv +import esphome.codegen as cg +from esphome.const import ( + CONF_ID, + CONF_NUMBER_DATAPOINT, + CONF_MAX_VALUE, + CONF_MIN_VALUE, + CONF_STEP, +) +from .. import tuya_ns, CONF_TUYA_ID, Tuya + +DEPENDENCIES = ["tuya"] +CODEOWNERS = ["@frankiboy1"] + +TuyaNumber = tuya_ns.class_("TuyaNumber", number.Number, cg.Component) + + +def validate_min_max(config): + if config[CONF_MAX_VALUE] <= config[CONF_MIN_VALUE]: + raise cv.Invalid("max_value must be greater than min_value") + return config + + +CONFIG_SCHEMA = cv.All( + number.NUMBER_SCHEMA.extend( + { + cv.GenerateID(): cv.declare_id(TuyaNumber), + cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya), + cv.Required(CONF_NUMBER_DATAPOINT): cv.uint8_t, + cv.Required(CONF_MAX_VALUE): cv.float_, + cv.Required(CONF_MIN_VALUE): cv.float_, + cv.Required(CONF_STEP): cv.positive_float, + } + ).extend(cv.COMPONENT_SCHEMA), + validate_min_max, +) + + +async def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + await cg.register_component(var, config) + await number.register_number( + var, + config, + min_value=config[CONF_MIN_VALUE], + max_value=config[CONF_MAX_VALUE], + step=config[CONF_STEP], + ) + + paren = await cg.get_variable(config[CONF_TUYA_ID]) + cg.add(var.set_tuya_parent(paren)) + + cg.add(var.set_number_id(config[CONF_NUMBER_DATAPOINT])) diff --git a/esphome/components/tuya/number/tuya_number.cpp b/esphome/components/tuya/number/tuya_number.cpp new file mode 100644 index 000000000..5c7cafbf7 --- /dev/null +++ b/esphome/components/tuya/number/tuya_number.cpp @@ -0,0 +1,38 @@ +#include "esphome/core/log.h" +#include "tuya_number.h" + +namespace esphome { +namespace tuya { + +static const char *const TAG = "tuya.number"; + +void TuyaNumber::setup() { + this->parent_->register_listener(this->number_id_, [this](const TuyaDatapoint &datapoint) { + if (datapoint.type == TuyaDatapointType::INTEGER) { + ESP_LOGV(TAG, "MCU reported number %u is: %d", datapoint.id, datapoint.value_int); + this->publish_state(datapoint.value_int); + } else if (datapoint.type == TuyaDatapointType::ENUM) { + ESP_LOGV(TAG, "MCU reported number %u is: %u", datapoint.id, datapoint.value_enum); + this->publish_state(datapoint.value_enum); + } + this->type_ = datapoint.type; + }); +} + +void TuyaNumber::control(float value) { + ESP_LOGV(TAG, "Setting number %u: %f", this->number_id_, value); + if (this->type_ == TuyaDatapointType::INTEGER) { + this->parent_->set_integer_datapoint_value(this->number_id_, value); + } else if (this->type_ == TuyaDatapointType::ENUM) { + this->parent_->set_enum_datapoint_value(this->number_id_, value); + } + this->publish_state(value); +} + +void TuyaNumber::dump_config() { + LOG_NUMBER("", "Tuya Number", this); + ESP_LOGCONFIG(TAG, " Number has datapoint ID %u", this->number_id_); +} + +} // namespace tuya +} // namespace esphome diff --git a/esphome/components/tuya/number/tuya_number.h b/esphome/components/tuya/number/tuya_number.h new file mode 100644 index 000000000..7cca9fc64 --- /dev/null +++ b/esphome/components/tuya/number/tuya_number.h @@ -0,0 +1,27 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/tuya/tuya.h" +#include "esphome/components/number/number.h" + +namespace esphome { +namespace tuya { + +class TuyaNumber : public number::Number, public Component { + public: + void setup() override; + void dump_config() override; + void set_number_id(uint8_t number_id) { this->number_id_ = number_id; } + + void set_tuya_parent(Tuya *parent) { this->parent_ = parent; } + + protected: + void control(float value) override; + + Tuya *parent_; + uint8_t number_id_{0}; + TuyaDatapointType type_{}; +}; + +} // namespace tuya +} // namespace esphome diff --git a/esphome/const.py b/esphome/const.py index 28648412a..970b3c657 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -402,6 +402,7 @@ CONF_NUM_CHIPS = "num_chips" CONF_NUM_LEDS = "num_leds" CONF_NUM_SCANS = "num_scans" CONF_NUMBER = "number" +CONF_NUMBER_DATAPOINT = "number_datapoint" CONF_OFF_MODE = "off_mode" CONF_OFFSET = "offset" CONF_ON = "on" diff --git a/tests/test4.yaml b/tests/test4.yaml index bb8f0f15c..545806ba8 100644 --- a/tests/test4.yaml +++ b/tests/test4.yaml @@ -441,7 +441,13 @@ display: wakeup_pin: GPIO1 vcom_pin: GPIO1 - +number: + - platform: tuya + id: tuya_number + number_datapoint: 102 + min_value: 0 + max_value: 17 + step: 1 text_sensor: - platform: pipsolar