Tuya text_sensor and raw data usage (#1812)

This commit is contained in:
dentra 2021-11-29 22:08:52 +03:00 committed by GitHub
parent b5639a6472
commit 939fb313df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 299 additions and 7 deletions

View file

@ -181,6 +181,7 @@ esphome/components/tuya/binary_sensor/* @jesserockz
esphome/components/tuya/climate/* @jesserockz
esphome/components/tuya/sensor/* @jesserockz
esphome/components/tuya/switch/* @jesserockz
esphome/components/tuya/text_sensor/* @dentra
esphome/components/uart/* @esphome/core
esphome/components/ultrasonic/* @OttoWinter
esphome/components/version/* @esphome/core

View file

@ -1,16 +1,84 @@
from esphome.components import time
from esphome import automation
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import uart
from esphome.const import CONF_ID, CONF_TIME_ID
from esphome.const import CONF_ID, CONF_TIME_ID, CONF_TRIGGER_ID, CONF_SENSOR_DATAPOINT
DEPENDENCIES = ["uart"]
CONF_IGNORE_MCU_UPDATE_ON_DATAPOINTS = "ignore_mcu_update_on_datapoints"
CONF_ON_DATAPOINT_UPDATE = "on_datapoint_update"
CONF_DATAPOINT_TYPE = "datapoint_type"
tuya_ns = cg.esphome_ns.namespace("tuya")
Tuya = tuya_ns.class_("Tuya", cg.Component, uart.UARTDevice)
DPTYPE_ANY = "any"
DPTYPE_RAW = "raw"
DPTYPE_BOOL = "bool"
DPTYPE_INT = "int"
DPTYPE_UINT = "uint"
DPTYPE_STRING = "string"
DPTYPE_ENUM = "enum"
DPTYPE_BITMASK = "bitmask"
DATAPOINT_TYPES = {
DPTYPE_ANY: tuya_ns.struct("TuyaDatapoint"),
DPTYPE_RAW: cg.std_vector.template(cg.uint8),
DPTYPE_BOOL: cg.bool_,
DPTYPE_INT: cg.int_,
DPTYPE_UINT: cg.uint32,
DPTYPE_STRING: cg.std_string,
DPTYPE_ENUM: cg.uint8,
DPTYPE_BITMASK: cg.uint32,
}
DATAPOINT_TRIGGERS = {
DPTYPE_ANY: tuya_ns.class_(
"TuyaDatapointUpdateTrigger",
automation.Trigger.template(DATAPOINT_TYPES[DPTYPE_ANY]),
),
DPTYPE_RAW: tuya_ns.class_(
"TuyaRawDatapointUpdateTrigger",
automation.Trigger.template(DATAPOINT_TYPES[DPTYPE_RAW]),
),
DPTYPE_BOOL: tuya_ns.class_(
"TuyaBoolDatapointUpdateTrigger",
automation.Trigger.template(DATAPOINT_TYPES[DPTYPE_BOOL]),
),
DPTYPE_INT: tuya_ns.class_(
"TuyaIntDatapointUpdateTrigger",
automation.Trigger.template(DATAPOINT_TYPES[DPTYPE_INT]),
),
DPTYPE_UINT: tuya_ns.class_(
"TuyaUIntDatapointUpdateTrigger",
automation.Trigger.template(DATAPOINT_TYPES[DPTYPE_UINT]),
),
DPTYPE_STRING: tuya_ns.class_(
"TuyaStringDatapointUpdateTrigger",
automation.Trigger.template(DATAPOINT_TYPES[DPTYPE_STRING]),
),
DPTYPE_ENUM: tuya_ns.class_(
"TuyaEnumDatapointUpdateTrigger",
automation.Trigger.template(DATAPOINT_TYPES[DPTYPE_ENUM]),
),
DPTYPE_BITMASK: tuya_ns.class_(
"TuyaBitmaskDatapointUpdateTrigger",
automation.Trigger.template(DATAPOINT_TYPES[DPTYPE_BITMASK]),
),
}
def assign_declare_id(value):
value = value.copy()
value[CONF_TRIGGER_ID] = cv.declare_id(
DATAPOINT_TRIGGERS[value[CONF_DATAPOINT_TYPE]]
)(value[CONF_TRIGGER_ID].id)
return value
CONF_TUYA_ID = "tuya_id"
CONFIG_SCHEMA = (
cv.Schema(
@ -20,6 +88,18 @@ CONFIG_SCHEMA = (
cv.Optional(CONF_IGNORE_MCU_UPDATE_ON_DATAPOINTS): cv.ensure_list(
cv.uint8_t
),
cv.Optional(CONF_ON_DATAPOINT_UPDATE): automation.validate_automation(
{
cv.GenerateID(CONF_TRIGGER_ID): cv.declare_id(
DATAPOINT_TRIGGERS[DPTYPE_ANY]
),
cv.Required(CONF_SENSOR_DATAPOINT): cv.uint8_t,
cv.Optional(CONF_DATAPOINT_TYPE, default=DPTYPE_ANY): cv.one_of(
*DATAPOINT_TRIGGERS, lower=True
),
},
extra_validators=assign_declare_id,
),
}
)
.extend(cv.COMPONENT_SCHEMA)
@ -37,3 +117,10 @@ async def to_code(config):
if CONF_IGNORE_MCU_UPDATE_ON_DATAPOINTS in config:
for dp in config[CONF_IGNORE_MCU_UPDATE_ON_DATAPOINTS]:
cg.add(var.add_ignore_mcu_update_on_datapoints(dp))
for conf in config.get(CONF_ON_DATAPOINT_UPDATE, []):
trigger = cg.new_Pvariable(
conf[CONF_TRIGGER_ID], var, conf[CONF_SENSOR_DATAPOINT]
)
await automation.build_automation(
trigger, [(DATAPOINT_TYPES[conf[CONF_DATAPOINT_TYPE]], "x")], conf
)

View file

@ -0,0 +1,66 @@
#include "esphome/core/log.h"
#include "automation.h"
static const char *const TAG = "tuya.automation";
namespace esphome {
namespace tuya {
void check_expected_datapoint(const TuyaDatapoint &dp, TuyaDatapointType expected) {
if (dp.type != expected) {
ESP_LOGW(TAG, "Tuya sensor %u expected datapoint type %#02hhX but got %#02hhX", dp.id, expected, dp.type);
}
}
TuyaRawDatapointUpdateTrigger::TuyaRawDatapointUpdateTrigger(Tuya *parent, uint8_t sensor_id) {
parent->register_listener(sensor_id, [this](const TuyaDatapoint &dp) {
check_expected_datapoint(dp, TuyaDatapointType::RAW);
this->trigger(dp.value_raw);
});
}
TuyaBoolDatapointUpdateTrigger::TuyaBoolDatapointUpdateTrigger(Tuya *parent, uint8_t sensor_id) {
parent->register_listener(sensor_id, [this](const TuyaDatapoint &dp) {
check_expected_datapoint(dp, TuyaDatapointType::BOOLEAN);
this->trigger(dp.value_bool);
});
}
TuyaIntDatapointUpdateTrigger::TuyaIntDatapointUpdateTrigger(Tuya *parent, uint8_t sensor_id) {
parent->register_listener(sensor_id, [this](const TuyaDatapoint &dp) {
check_expected_datapoint(dp, TuyaDatapointType::INTEGER);
this->trigger(dp.value_int);
});
}
TuyaUIntDatapointUpdateTrigger::TuyaUIntDatapointUpdateTrigger(Tuya *parent, uint8_t sensor_id) {
parent->register_listener(sensor_id, [this](const TuyaDatapoint &dp) {
check_expected_datapoint(dp, TuyaDatapointType::INTEGER);
this->trigger(dp.value_uint);
});
}
TuyaStringDatapointUpdateTrigger::TuyaStringDatapointUpdateTrigger(Tuya *parent, uint8_t sensor_id) {
parent->register_listener(sensor_id, [this](const TuyaDatapoint &dp) {
check_expected_datapoint(dp, TuyaDatapointType::STRING);
this->trigger(dp.value_string);
});
}
TuyaEnumDatapointUpdateTrigger::TuyaEnumDatapointUpdateTrigger(Tuya *parent, uint8_t sensor_id) {
parent->register_listener(sensor_id, [this](const TuyaDatapoint &dp) {
check_expected_datapoint(dp, TuyaDatapointType::ENUM);
this->trigger(dp.value_enum);
});
}
TuyaBitmaskDatapointUpdateTrigger::TuyaBitmaskDatapointUpdateTrigger(Tuya *parent, uint8_t sensor_id) {
parent->register_listener(sensor_id, [this](const TuyaDatapoint &dp) {
check_expected_datapoint(dp, TuyaDatapointType::BITMASK);
this->trigger(dp.value_bitmask);
});
}
} // namespace tuya
} // namespace esphome

View file

@ -0,0 +1,53 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/core/automation.h"
#include "tuya.h"
namespace esphome {
namespace tuya {
class TuyaDatapointUpdateTrigger : public Trigger<TuyaDatapoint> {
public:
explicit TuyaDatapointUpdateTrigger(Tuya *parent, uint8_t sensor_id) {
parent->register_listener(sensor_id, [this](const TuyaDatapoint &dp) { this->trigger(dp); });
}
};
class TuyaRawDatapointUpdateTrigger : public Trigger<std::vector<uint8_t>> {
public:
explicit TuyaRawDatapointUpdateTrigger(Tuya *parent, uint8_t sensor_id);
};
class TuyaBoolDatapointUpdateTrigger : public Trigger<bool> {
public:
explicit TuyaBoolDatapointUpdateTrigger(Tuya *parent, uint8_t sensor_id);
};
class TuyaIntDatapointUpdateTrigger : public Trigger<int> {
public:
explicit TuyaIntDatapointUpdateTrigger(Tuya *parent, uint8_t sensor_id);
};
class TuyaUIntDatapointUpdateTrigger : public Trigger<uint32_t> {
public:
explicit TuyaUIntDatapointUpdateTrigger(Tuya *parent, uint8_t sensor_id);
};
class TuyaStringDatapointUpdateTrigger : public Trigger<std::string> {
public:
explicit TuyaStringDatapointUpdateTrigger(Tuya *parent, uint8_t sensor_id);
};
class TuyaEnumDatapointUpdateTrigger : public Trigger<uint8_t> {
public:
explicit TuyaEnumDatapointUpdateTrigger(Tuya *parent, uint8_t sensor_id);
};
class TuyaBitmaskDatapointUpdateTrigger : public Trigger<uint32_t> {
public:
explicit TuyaBitmaskDatapointUpdateTrigger(Tuya *parent, uint8_t sensor_id);
};
} // namespace tuya
} // namespace esphome

View file

@ -1,14 +1,12 @@
from esphome.components import binary_sensor
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.const import CONF_ID
from esphome.const import CONF_ID, CONF_SENSOR_DATAPOINT
from .. import tuya_ns, CONF_TUYA_ID, Tuya
DEPENDENCIES = ["tuya"]
CODEOWNERS = ["@jesserockz"]
CONF_SENSOR_DATAPOINT = "sensor_datapoint"
TuyaBinarySensor = tuya_ns.class_(
"TuyaBinarySensor", binary_sensor.BinarySensor, cg.Component
)

View file

@ -1,14 +1,12 @@
from esphome.components import sensor
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.const import CONF_ID
from esphome.const import CONF_ID, CONF_SENSOR_DATAPOINT
from .. import tuya_ns, CONF_TUYA_ID, Tuya
DEPENDENCIES = ["tuya"]
CODEOWNERS = ["@jesserockz"]
CONF_SENSOR_DATAPOINT = "sensor_datapoint"
TuyaSensor = tuya_ns.class_("TuyaSensor", sensor.Sensor, cg.Component)
CONFIG_SCHEMA = sensor.SENSOR_SCHEMA.extend(

View file

@ -0,0 +1,29 @@
from esphome.components import text_sensor
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.const import CONF_ID, CONF_SENSOR_DATAPOINT
from .. import tuya_ns, CONF_TUYA_ID, Tuya
DEPENDENCIES = ["tuya"]
CODEOWNERS = ["@dentra"]
TuyaTextSensor = tuya_ns.class_("TuyaTextSensor", text_sensor.TextSensor, cg.Component)
CONFIG_SCHEMA = text_sensor.TEXT_SENSOR_SCHEMA.extend(
{
cv.GenerateID(): cv.declare_id(TuyaTextSensor),
cv.GenerateID(CONF_TUYA_ID): cv.use_id(Tuya),
cv.Required(CONF_SENSOR_DATAPOINT): cv.uint8_t,
}
).extend(cv.COMPONENT_SCHEMA)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await text_sensor.register_text_sensor(var, config)
paren = await cg.get_variable(config[CONF_TUYA_ID])
cg.add(var.set_tuya_parent(paren))
cg.add(var.set_sensor_id(config[CONF_SENSOR_DATAPOINT]))

View file

@ -0,0 +1,35 @@
#include "esphome/core/log.h"
#include "tuya_text_sensor.h"
namespace esphome {
namespace tuya {
static const char *const TAG = "tuya.text_sensor";
void TuyaTextSensor::setup() {
this->parent_->register_listener(this->sensor_id_, [this](const TuyaDatapoint &datapoint) {
switch (datapoint.type) {
case TuyaDatapointType::STRING:
ESP_LOGD(TAG, "MCU reported text sensor %u is: %s", datapoint.id, datapoint.value_string.c_str());
this->publish_state(datapoint.value_string);
break;
case TuyaDatapointType::RAW: {
std::string data = hexencode(datapoint.value_raw);
ESP_LOGD(TAG, "MCU reported text sensor %u is: %s", datapoint.id, data.c_str());
this->publish_state(data);
break;
}
default:
ESP_LOGW(TAG, "Unsupported data type for tuya text sensor %u: %#02hhX", datapoint.id, datapoint.type);
break;
}
});
}
void TuyaTextSensor::dump_config() {
ESP_LOGCONFIG(TAG, "Tuya Text Sensor:");
ESP_LOGCONFIG(TAG, " Text Sensor has datapoint ID %u", this->sensor_id_);
}
} // namespace tuya
} // namespace esphome

View file

@ -0,0 +1,24 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/tuya/tuya.h"
#include "esphome/components/text_sensor/text_sensor.h"
namespace esphome {
namespace tuya {
class TuyaTextSensor : public text_sensor::TextSensor, public Component {
public:
void setup() override;
void dump_config() override;
void set_sensor_id(uint8_t sensor_id) { this->sensor_id_ = sensor_id; }
void set_tuya_parent(Tuya *parent) { this->parent_ = parent; }
protected:
Tuya *parent_;
uint8_t sensor_id_{0};
};
} // namespace tuya
} // namespace esphome

View file

@ -579,6 +579,7 @@ CONF_SEND_EVERY = "send_every"
CONF_SEND_FIRST_AT = "send_first_at"
CONF_SENSING_PIN = "sensing_pin"
CONF_SENSOR = "sensor"
CONF_SENSOR_DATAPOINT = "sensor_datapoint"
CONF_SENSOR_ID = "sensor_id"
CONF_SENSORS = "sensors"
CONF_SEQUENCE = "sequence"