This commit is contained in:
Giovanni Condello 2024-05-02 15:47:52 +12:00 committed by GitHub
commit 77b1cb5974
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 631 additions and 0 deletions

View File

@ -403,6 +403,7 @@ esphome/components/version/* @esphome/core
esphome/components/voice_assistant/* @jesserockz
esphome/components/wake_on_lan/* @willwill2will54
esphome/components/waveshare_epaper/* @clydebarrow
esphome/components/waveshare_epaper_1in9_i2c/* @nanomad
esphome/components/web_server_base/* @OttoWinter
esphome/components/web_server_idf/* @dentra
esphome/components/weikai/* @DrCoolZic

View File

@ -0,0 +1 @@
CODEOWNERS = ["@nanomad"]

View File

@ -0,0 +1,76 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome import pins
from esphome.components import display, i2c
from esphome.const import (
CONF_BUSY_PIN,
CONF_FULL_UPDATE_EVERY,
CONF_I2C_ID,
CONF_ID,
CONF_LAMBDA,
CONF_RESET_PIN,
)
UNITS = {
"f": "f",
"c": "c",
}
DEPENDENCIES = ["i2c"]
waveshare_epaper_1in9_i2c_ns = cg.esphome_ns.namespace("waveshare_epaper_1in9_i2c")
WaveShareEPaper1in9I2C = waveshare_epaper_1in9_i2c_ns.class_(
"WaveShareEPaper1in9I2C", cg.PollingComponent
)
WaveShareEPaper1in9I2CRef = WaveShareEPaper1in9I2C.operator("ref")
CONF_COMMAND_ADDRESS = "command_address"
CONF_DATA_ADDRESS = "data_address"
CONF_TEMPERATURE_UNIT = "temperature_unit"
CONFIG_SCHEMA = display.BASIC_DISPLAY_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(WaveShareEPaper1in9I2C),
cv.Required(CONF_RESET_PIN): pins.gpio_output_pin_schema,
cv.Required(CONF_BUSY_PIN): pins.gpio_input_pin_schema,
cv.GenerateID(CONF_I2C_ID): cv.use_id(i2c.I2CBus),
cv.Optional(CONF_COMMAND_ADDRESS, default=0x3C): cv.int_range(min=0, max=0xFF),
cv.Optional(CONF_DATA_ADDRESS, default=0x3D): cv.int_range(min=0, max=0xFF),
cv.Optional(CONF_FULL_UPDATE_EVERY): cv.uint32_t,
cv.Optional(CONF_TEMPERATURE_UNIT, default="c"): cv.enum(UNITS),
}
).extend(cv.polling_component_schema("1s"))
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await display.register_display(var, config)
reset_pin = await cg.gpio_pin_expression(config[CONF_RESET_PIN])
cg.add(var.set_reset_pin(reset_pin))
busy_pin = await cg.gpio_pin_expression(config[CONF_BUSY_PIN])
cg.add(var.set_busy_pin(busy_pin))
i2c_bus = await cg.get_variable(config[CONF_I2C_ID])
command_address = config[CONF_COMMAND_ADDRESS]
cg.add(var.create_command_device(i2c_bus, command_address))
data_address = config[CONF_DATA_ADDRESS]
cg.add(var.create_data_device(i2c_bus, data_address))
temperature_unit = config[CONF_TEMPERATURE_UNIT]
cg.add(var.set_temperature_unit(temperature_unit))
if CONF_FULL_UPDATE_EVERY in config:
cg.add(var.set_full_update_every(config[CONF_FULL_UPDATE_EVERY]))
if CONF_LAMBDA in config:
lambda_ = await cg.process_lambda(
config[CONF_LAMBDA],
[(WaveShareEPaper1in9I2CRef, "it")],
return_type=cg.void,
)
cg.add(var.set_writer(lambda_))

View File

@ -0,0 +1,66 @@
#pragma once
#include "esphome/core/hal.h"
#include "i2c_commands.h"
namespace esphome {
namespace waveshare_epaper_1in9_i2c {
static const int16_t TEMPERATURE_X10_MIN = -99;
static const int16_t TEMPERATURE_X10_MAX = 1999;
static const int16_t HUMIDITY_X10_MIN = -99;
static const int16_t HUMIDITY_X10_MAX = 999;
static const unsigned LUT_SIZE = 7;
// 5S waveform for better anti-ghosting
static const uint8_t LUT_5S[LUT_SIZE] = {CMD_WAVEFORM_SET, 0x28, 0x20, 0xA8, 0xA0, 0x50, 0x65};
// White extinction diagram + black out diagram
static const uint8_t LUT_DU_WB[LUT_SIZE] = {CMD_WAVEFORM_SET, 0x80, 0x00, 0xC0, 0x80, 0x80, 0x62};
static const unsigned FRAMEBUFFER_SIZE = 15;
static const uint8_t CHAR_EMPTY = 0x00;
static const uint8_t CHAR_CELSIUS = 0x05;
static const uint8_t CHAR_FAHRENHEIT = 0x06;
static const unsigned CHAR_SLOTS = 2;
static const uint8_t CHAR_MINUS_SIGN[CHAR_SLOTS] = {0b01000100, 0b00000};
static const uint8_t CHAR_DIGITS[10][CHAR_SLOTS] = {
{0xbf, 0xff}, // 0
{0x00, 0xff}, // 1
{0xfd, 0x17}, // 2
{0xf5, 0xff}, // 3
{0x47, 0xff}, // 4
{0xf7, 0x1d}, // 5
{0xff, 0x1d}, // 6
{0x21, 0xff}, // 7
{0xff, 0xff}, // 8
{0xf7, 0xff}, // 9
};
static const unsigned TEMPERATURE_DIGITS_LEN = 4;
static const unsigned HUMIDITY_DIGITS_LEN = 3;
static const unsigned TEMPERATURE_DOT_INDEX = 4;
static const unsigned HUMIDITY_DOT_IDX = 8;
static const unsigned HUMIDITY_PERCENTAGE_IDX = 10;
static const unsigned INDICATORS_IDX = 13; // Framebuffer position for °C/F/low power/BT indicators
// Bitmask to show the decimal dot
static const unsigned DOT_MASK = 0b0000000000100000;
// Bitmask to show humidity % sign
static const unsigned PERCENT_MASK = 0b0000000000100000;
// Bitmask to enable the low power indicator (empty battery symbol)
static const unsigned LOW_POWER_ON_MASK = 0b0000000000010000;
// Bitmask to enable the BT indicator
static const unsigned BT_ON_MASK = 0b0000000000001000;
} // namespace waveshare_epaper_1in9_i2c
} // namespace esphome

View File

@ -0,0 +1,12 @@
#pragma once
#include "esphome/core/helpers.h"
#include "display_constants.h"
namespace esphome {
namespace waveshare_epaper_1in9_i2c {
static inline uint8_t get_pixel(int number, int order) { return number < 0 ? CHAR_EMPTY : CHAR_DIGITS[number][order]; }
} // namespace waveshare_epaper_1in9_i2c
} // namespace esphome

View File

@ -0,0 +1,30 @@
#pragma once
#include "esphome/core/hal.h"
namespace esphome {
namespace waveshare_epaper_1in9_i2c {
static const uint8_t CMD_POWER_OFF = 0x28;
static const uint8_t CMD_POWER_ON = 0x2B;
static const uint8_t CMD_RAM_ADDR_0 = 0x40;
static const uint8_t CMD_WAVEFORM_SET = 0x82;
static const uint8_t CMD_DCDC_BOOST_X8 = 0xA7;
static const uint8_t CMD_DATA_1_LATCH_OFF = 0xA8;
static const uint8_t CMD_DATA_1_LATCH_ON = 0xA9;
static const uint8_t CMD_DATA_2_LATCH_OFF = 0xAA;
static const uint8_t CMD_DATA_2_LATCH_ON = 0xAB;
static const uint8_t CMD_SLEEP_OFF = 0xAC;
static const uint8_t CMD_SLEEP_ON = 0xAD;
static const uint8_t CMD_DISPLAY_OFF = 0xAE;
static const uint8_t CMD_DISPLAY_ON = 0xAF;
} // namespace waveshare_epaper_1in9_i2c
} // namespace esphome

View File

@ -0,0 +1,305 @@
#include "waveshare_epaper_1in9_i2c.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "i2c_commands.h"
#include "display_utils.h"
namespace esphome {
namespace waveshare_epaper_1in9_i2c {
static const char *const TAG = "waveshare_epaper_1in9_i2c";
void WaveShareEPaper1in9I2C::setup() {
ESP_LOGD(TAG, "Setting up WaveShareEPaper1in9I2C...");
this->reset_pin_->setup();
this->busy_pin_->setup();
this->init_screen_();
this->write_lut_(LUT_5S);
this->wait_for_idle_();
this->display();
}
void WaveShareEPaper1in9I2C::update() {
ESP_LOGV(TAG, "WaveShareEPaper1in9I2C::update Called...");
if (this->writer_.has_value()) {
(*this->writer_)(*this);
}
this->display();
}
void HOT WaveShareEPaper1in9I2C::display() {
if (this->display_state_.is_changed()) {
this->display_state_.flip();
ESP_LOGD(TAG, "WaveShareEPaper1in9I2C::update Display has changed, performing a refresh...");
int16_t temperature = this->display_state_.temperature_x10.current;
bool valid_temperature = temperature >= TEMPERATURE_X10_MIN && temperature <= TEMPERATURE_X10_MAX;
bool temperature_minus_sign = valid_temperature && temperature < 0;
temperature = abs(temperature);
int16_t humidity = this->display_state_.humidity_x10.current;
bool valid_humidity = humidity >= HUMIDITY_X10_MIN && humidity <= HUMIDITY_X10_MAX;
bool humidity_minus_sign = valid_humidity && humidity < 0;
humidity = abs(humidity);
int temperature_digits[TEMPERATURE_DIGITS_LEN] = {-1, -1, -1, -1};
int humidity_digits[HUMIDITY_DIGITS_LEN] = {-1, -1, -1};
if (valid_temperature) {
ESP_LOGV(TAG, "WaveShareEPaper1in9I2C::update Temperature is valid, extracting digits (%d)...", temperature);
temperature_digits[3] = temperature % 10;
temperature_digits[2] = (temperature % 100) / 10;
temperature_digits[1] = (temperature % 1000) / 100;
temperature_digits[0] = temperature / 1000;
// Hide leading zeros
if (temperature_digits[0] == 0) {
temperature_digits[0] = -1;
if (temperature_digits[1] == 0) {
temperature_digits[1] = -1;
}
}
ESP_LOGVV(TAG, "WaveShareEPaper1in9I2C::update Temperature digits: %d %d %d %d", temperature_digits[0],
temperature_digits[1], temperature_digits[2], temperature_digits[3]);
}
if (valid_humidity) {
ESP_LOGV(TAG, "WaveShareEPaper1in9I2C::update Humidity is valid, extracting digits (%d)...", humidity);
humidity_digits[2] = humidity % 10;
humidity_digits[1] = (humidity % 100) / 10;
humidity_digits[0] = humidity / 100;
// Hide leading zeros
if (humidity_digits[0] == 0) {
humidity_digits[0] = -1;
}
ESP_LOGVV(TAG, "WaveShareEPaper1in9I2C::update Humidity digits: %d %d %d", humidity_digits[0], humidity_digits[1],
humidity_digits[2]);
}
uint8_t new_image[FRAMEBUFFER_SIZE] = {
temperature_minus_sign ? CHAR_EMPTY : get_pixel(temperature_digits[0], 1),
temperature_minus_sign ? CHAR_MINUS_SIGN[0] : get_pixel(temperature_digits[1], 0),
temperature_minus_sign ? CHAR_MINUS_SIGN[1] : get_pixel(temperature_digits[1], 1),
get_pixel(temperature_digits[2], 0),
get_pixel(temperature_digits[2], 1),
humidity_minus_sign ? CHAR_MINUS_SIGN[0] : get_pixel(humidity_digits[0], 0),
humidity_minus_sign ? CHAR_MINUS_SIGN[1] : get_pixel(humidity_digits[0], 1),
get_pixel(humidity_digits[1], 0),
get_pixel(humidity_digits[1], 1),
get_pixel(humidity_digits[2], 0),
get_pixel(humidity_digits[2], 1),
get_pixel(temperature_digits[3], 0),
get_pixel(temperature_digits[3], 1),
CHAR_EMPTY, // С°/F°/power/bluetooth
CHAR_EMPTY // Position 14 must always be empty
};
if (valid_temperature) {
new_image[TEMPERATURE_DOT_INDEX] |= DOT_MASK;
if (this->display_state_.display_temperature_unit.current) {
new_image[INDICATORS_IDX] |= this->display_state_.is_celsius.current ? CHAR_CELSIUS : CHAR_FAHRENHEIT;
}
}
if (valid_humidity) {
new_image[HUMIDITY_DOT_IDX] |= DOT_MASK;
if (this->display_state_.display_percent.current) {
new_image[HUMIDITY_PERCENTAGE_IDX] |= PERCENT_MASK;
}
}
if (this->display_state_.low_power.current) {
new_image[INDICATORS_IDX] |= LOW_POWER_ON_MASK;
}
if (this->display_state_.bluetooth.current) {
new_image[INDICATORS_IDX] |= BT_ON_MASK;
}
bool partial_refresh = this->at_update_ != 0;
this->at_update_ = (this->at_update_ + 1) % this->full_update_every_;
ESP_LOGD(TAG, "WaveShareEPaper1in9I2C::refreshing...");
if (partial_refresh) {
this->write_lut_(LUT_DU_WB);
} else {
this->write_lut_(LUT_5S);
}
this->wait_for_idle_();
this->write_screen_(new_image);
}
this->deep_sleep_();
}
void WaveShareEPaper1in9I2C::reset_screen_() {
ESP_LOGV(TAG, "WaveShareEPaper1in9I2C::reset_screen");
this->send_reset_(true);
delay(200); // NOLINT
this->send_reset_(false);
delay(20);
this->send_reset_(true);
delay(200); // NOLINT
}
/**
* Wait until the busy_pin goes LOW
**/
void HOT WaveShareEPaper1in9I2C::wait_for_idle_() {
ESP_LOGVV(TAG, "WaveShareEPaper1in9I2C::wait_for_idle_... waiting for screen idle");
while (this->busy_pin_->digital_read() != 1) { //=1 BUSY;
delay(10);
}
ESP_LOGVV(TAG, "WaveShareEPaper1in9I2C::wait_for_idle_... screen no longer busy");
}
void WaveShareEPaper1in9I2C::init_screen_() {
ESP_LOGD(TAG, "WaveShareEPaper1in9I2C::init_screen...");
this->reset_screen_();
this->wait_for_idle_();
this->send_commands_(&CMD_POWER_ON, 1);
delay(10);
uint8_t data[] = {
CMD_DCDC_BOOST_X8, // DCDC Boost x8
0xE8 // TSON
};
this->send_commands_(data, 2);
delay(10);
this->apply_temperature_compensation();
}
void WaveShareEPaper1in9I2C::apply_temperature_compensation() {
ESP_LOGVV(TAG, "WaveShareEPaper1in9I2C::apply_temperature_compensation...");
if (this->compensation_temp_ < 10) {
uint8_t data[] = {0x7E, 0x81, 0xB4};
this->send_commands_(data, 3);
} else {
uint8_t data[] = {0x7E, 0x81, 0xB4};
this->send_commands_(data, 3);
}
delay(10);
uint8_t frame_time;
if (this->compensation_temp_ < 5) {
frame_time = 0x31; // (49+1)*20ms=1000ms
} else if (this->compensation_temp_ < 10) {
frame_time = 0x22; // (34+1)*20ms=700ms
} else if (this->compensation_temp_ < 15) {
frame_time = 0x18; // (24+1)*20ms=500ms;
} else if (this->compensation_temp_ < 20) {
frame_time = 0x13; // (19+1)*20ms=400ms
} else {
frame_time = 0x0e; // (14+1)*20ms=300ms
}
uint8_t data[] = {
0xe7, // Set default frame time
frame_time // Computed frame time
};
this->send_commands_(data, 2);
}
void WaveShareEPaper1in9I2C::write_lut_(const uint8_t lut[LUT_SIZE]) {
ESP_LOGVV(TAG, "WaveShareEPaper1in9I2C::write_lut...");
this->send_commands_(lut, LUT_SIZE);
}
void WaveShareEPaper1in9I2C::write_screen_(const uint8_t framebuffer[FRAMEBUFFER_SIZE]) {
ESP_LOGV(TAG, "WaveShareEPaper1in9I2C::write_screen...");
uint8_t before_write_data[] = {
CMD_SLEEP_OFF, // Close the sleep
CMD_POWER_ON, // Turn on the power
CMD_RAM_ADDR_0, // Write RAM address
CMD_DATA_1_LATCH_ON, // Turn on the first SRAM
CMD_DATA_1_LATCH_OFF // Shut down the first SRAM
};
this->send_commands_(before_write_data, 5);
// Write the image to the screen
this->send_data_(framebuffer, FRAMEBUFFER_SIZE);
uint8_t bg_color = this->inverted_colors_ ? 0x03 : 0x00;
this->send_data_(&bg_color, 1);
uint8_t after_write_data_1[] = {
CMD_DATA_2_LATCH_ON, // Turn on the second SRAM
CMD_DATA_2_LATCH_OFF, // Shut down the second SRAM
CMD_DISPLAY_ON // display on
};
this->send_commands_(after_write_data_1, 3);
this->wait_for_idle_();
uint8_t after_write_data_2[] = {
CMD_DISPLAY_OFF, // Display off
CMD_POWER_OFF, // HV OFF
CMD_SLEEP_ON // Sleep in
};
this->send_commands_(after_write_data_2, 3);
}
void WaveShareEPaper1in9I2C::deep_sleep_() {
this->send_commands_(&CMD_POWER_OFF, 1);
this->wait_for_idle_();
this->send_commands_(&CMD_SLEEP_ON, 1);
}
void WaveShareEPaper1in9I2C::set_temperature_unit(const char *unit) {
this->display_state_.is_celsius.future = (strcasecmp(unit, "c") == 0);
}
void WaveShareEPaper1in9I2C::set_temperature(float temperature) {
this->display_state_.temperature_x10.future = (int16_t) 10.0 * temperature;
}
void WaveShareEPaper1in9I2C::set_humidity(float humidity) {
this->display_state_.humidity_x10.future = (int16_t) 10.0 * humidity;
}
void WaveShareEPaper1in9I2C::display_low_power_indicator(bool is_low_power) {
this->display_state_.low_power.future = is_low_power;
}
void WaveShareEPaper1in9I2C::display_bluetooth_indicator(bool is_bluetooth) {
this->display_state_.bluetooth.future = is_bluetooth;
}
void WaveShareEPaper1in9I2C::display_percent(bool display_percent) {
this->display_state_.display_percent.future = display_percent;
}
void WaveShareEPaper1in9I2C::display_temperature_unit(bool display_temperature_unit) {
this->display_state_.display_temperature_unit.future = display_temperature_unit;
}
void WaveShareEPaper1in9I2C::dump_config() {
ESP_LOGCONFIG(TAG, "Waveshare E-Paper 1.9in I2C");
ESP_LOGCONFIG(TAG, " I2C Command Address: 0x%02X", this->command_device_address_);
ESP_LOGCONFIG(TAG, " I2C Data Address: 0x%02X", this->data_device_address_);
LOG_PIN(" Reset Pin: ", this->reset_pin_);
LOG_PIN(" Busy Pin: ", this->busy_pin_);
LOG_UPDATE_INTERVAL(this);
}
void WaveShareEPaper1in9I2C::send_commands_(const uint8_t *data, uint8_t len, bool stop) {
this->command_device_.write(data, len, stop);
}
void WaveShareEPaper1in9I2C::send_data_(const uint8_t *data, uint8_t len, bool stop) {
this->data_device_.write(data, len, stop);
}
} // namespace waveshare_epaper_1in9_i2c
} // namespace esphome

View File

@ -0,0 +1,120 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "esphome/components/i2c/i2c.h"
#include "display_constants.h"
namespace esphome {
namespace waveshare_epaper_1in9_i2c {
template<typename T> struct ValueState {
T current;
T future;
bool is_changed() { return this->current != this->future; }
void flip() { this->current = this->future; }
};
struct DisplayState {
ValueState<int16_t> temperature_x10{TEMPERATURE_X10_MAX + 1, TEMPERATURE_X10_MAX + 1};
ValueState<int16_t> humidity_x10{HUMIDITY_X10_MAX + 1, HUMIDITY_X10_MAX + 1};
ValueState<bool> low_power{false, false};
ValueState<bool> bluetooth{false, false};
ValueState<bool> is_celsius{true, true};
ValueState<bool> display_percent{true, true};
ValueState<bool> display_temperature_unit{true, true};
bool is_changed() {
return this->temperature_x10.is_changed() || this->humidity_x10.is_changed() || this->low_power.is_changed() ||
this->bluetooth.is_changed() || this->is_celsius.is_changed() || this->display_percent.is_changed() ||
this->display_temperature_unit.is_changed();
}
void flip() {
this->temperature_x10.flip();
this->humidity_x10.flip();
this->low_power.flip();
this->bluetooth.flip();
this->is_celsius.flip();
this->display_percent.flip();
this->display_temperature_unit.flip();
}
};
class WaveShareEPaper1in9I2C;
using waveshare_epaper_1in9_i2c_writer_t = std::function<void(WaveShareEPaper1in9I2C &)>;
class WaveShareEPaper1in9I2C : public PollingComponent {
public:
void set_writer(waveshare_epaper_1in9_i2c_writer_t &&writer) { this->writer_ = writer; }
void setup() override;
void update() override;
void display();
float get_setup_priority() const override { return setup_priority::PROCESSOR; };
void create_command_device(i2c::I2CBus *bus, uint8_t address) {
this->command_device_.set_i2c_address(address);
this->command_device_.set_i2c_bus(bus);
this->command_device_address_ = address;
}
void create_data_device(i2c::I2CBus *bus, uint8_t address) {
this->data_device_.set_i2c_address(address);
this->data_device_.set_i2c_bus(bus);
this->data_device_address_ = address;
}
void set_reset_pin(GPIOPin *reset_pin) { this->reset_pin_ = reset_pin; }
void set_busy_pin(GPIOPin *busy_pin) { this->busy_pin_ = busy_pin; }
void set_full_update_every(uint32_t full_update_every) { this->full_update_every_ = full_update_every; }
void set_temperature_for_compensation(float temp) { this->compensation_temp_ = temp; }
void set_temperature(float temp);
void set_temperature_unit(const char *unit);
void set_humidity(float humidity);
void display_percent(bool display_percent);
void display_temperature_unit(bool display_temperature_unit);
void display_low_power_indicator(bool is_low_power);
void display_bluetooth_indicator(bool is_bluetooth);
void apply_temperature_compensation();
void dump_config() override;
protected:
float compensation_temp_{20};
bool inverted_colors_{false};
DisplayState display_state_;
uint32_t full_update_every_{30};
uint32_t at_update_{0};
uint8_t command_device_address_;
i2c::I2CDevice command_device_;
uint8_t data_device_address_;
i2c::I2CDevice data_device_;
GPIOPin *reset_pin_;
GPIOPin *busy_pin_;
optional<waveshare_epaper_1in9_i2c_writer_t> writer_{};
void init_screen_();
void reset_screen_();
void wait_for_idle_();
void write_lut_(const uint8_t lut[LUT_SIZE]);
void write_screen_(const uint8_t framebuffer[FRAMEBUFFER_SIZE]);
void deep_sleep_();
void send_commands_(const uint8_t *data, uint8_t len, bool stop = true);
void send_data_(const uint8_t *data, uint8_t len, bool stop = true);
void send_reset_(bool value) { this->reset_pin_->digital_write(value); };
};
} // namespace waveshare_epaper_1in9_i2c
} // namespace esphome

View File

@ -666,6 +666,26 @@ display:
number: GPIO1
allow_other_uses: true
- platform: waveshare_epaper_1in9_i2c
command_address: 0x3C
data_address: 0x3D
busy_pin:
number: GPIO23
allow_other_uses: true
reset_pin:
number: GPIO23
allow_other_uses: true
update_interval: 5s
full_update_every: 30
lambda: |-
static float value1 = 218.3;
it.display_low_power_indicator(true);
it.display_bluetooth_indicator(true);
it.display_temperature_unit(true);
it.display_percent(true);
it.set_temperature(value1);
it.set_humidity(value1);
number:
- platform: tuya
id: tuya_number