mirror of
https://github.com/esphome/esphome.git
synced 2026-10-08 03:54:03 +00:00
[zigbee] Add zigbee support for number on esp32 (#20243)
This commit is contained in:
@@ -42,6 +42,7 @@ from .const_zephyr import (
|
||||
from .zigbee_esp32 import (
|
||||
final_validate_esp32,
|
||||
validate_binary_sensor_esp32,
|
||||
validate_number_esp32,
|
||||
validate_sensor_esp32,
|
||||
validate_switch_esp32,
|
||||
zigbee_require_vfs_select,
|
||||
@@ -120,7 +121,9 @@ SENSOR_SCHEMA = (
|
||||
SWITCH_SCHEMA = (
|
||||
cv.Schema({}).extend(_get_base_schema(["generic", "on_off"])).extend(zephyr_switch)
|
||||
)
|
||||
NUMBER_SCHEMA = cv.Schema({}).extend(zephyr_number)
|
||||
NUMBER_SCHEMA = (
|
||||
cv.Schema({}).extend(_get_base_schema(["generic"])).extend(zephyr_number)
|
||||
)
|
||||
|
||||
|
||||
def _validate_router_sleepy(config: ConfigType) -> ConfigType:
|
||||
@@ -269,12 +272,18 @@ async def setup_number(
|
||||
max_value: float,
|
||||
step: float,
|
||||
) -> None:
|
||||
if not config.get(CONF_ZIGBEE_ID) or config.get(CONF_INTERNAL):
|
||||
if "zigbee" not in CORE.loaded_integrations or config.get(CONF_INTERNAL):
|
||||
return
|
||||
if CORE.using_zephyr:
|
||||
if not config.get(CONF_ZIGBEE_ID):
|
||||
return
|
||||
from .zigbee_zephyr import zephyr_setup_number
|
||||
|
||||
await zephyr_setup_number(entity, config, min_value, max_value, step)
|
||||
else:
|
||||
from .zigbee_esp32 import add_component as add_number
|
||||
|
||||
CORE.add_job(add_number, entity, config)
|
||||
|
||||
|
||||
def consume_endpoint(config: ConfigType) -> ConfigType:
|
||||
@@ -320,7 +329,7 @@ def validate_number(config: ConfigType) -> ConfigType:
|
||||
if "zigbee" not in CORE.loaded_integrations or config.get(CONF_INTERNAL):
|
||||
return config
|
||||
if CORE.is_esp32:
|
||||
return config
|
||||
return validate_number_esp32(config)
|
||||
return consume_endpoint(config)
|
||||
|
||||
|
||||
|
||||
@@ -145,7 +145,8 @@ class AnalogInputType(IntEnum):
|
||||
TIME_SECONDS = 0x0E
|
||||
|
||||
|
||||
ANALOG_INPUT_APPTYPE = {
|
||||
# Same for input and output
|
||||
ANALOG_APPTYPE = {
|
||||
(DEVICE_CLASS_TEMPERATURE, UNIT_CELSIUS): AnalogInputType.TEMP_DEGREES_C,
|
||||
(DEVICE_CLASS_HUMIDITY, UNIT_PERCENT): AnalogInputType.RELATIVE_HUMIDITY_PERCENT,
|
||||
(DEVICE_CLASS_PRESSURE, UNIT_PASCAL): AnalogInputType.PRESSURE_PASCAL,
|
||||
|
||||
@@ -33,6 +33,7 @@ CLUSTER_ID = {
|
||||
"BINARY_INPUT": cluster_id.EZB_ZCL_CLUSTER_ID_BINARY_INPUT,
|
||||
"BINARY_OUTPUT": cluster_id.EZB_ZCL_CLUSTER_ID_BINARY_OUTPUT,
|
||||
"ANALOG_INPUT": cluster_id.EZB_ZCL_CLUSTER_ID_ANALOG_INPUT,
|
||||
"ANALOG_OUTPUT": cluster_id.EZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT,
|
||||
"ILLUMINANCE_MEASUREMENT": cluster_id.EZB_ZCL_CLUSTER_ID_ILLUMINANCE_MEASUREMENT,
|
||||
"TEMPERATURE_MEASUREMENT": cluster_id.EZB_ZCL_CLUSTER_ID_TEMPERATURE_MEASUREMENT,
|
||||
"PRESSURE_MEASUREMENT": cluster_id.EZB_ZCL_CLUSTER_ID_PRESSURE_MEASUREMENT,
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
#ifdef USE_SWITCH
|
||||
#include "esphome/components/switch/switch.h"
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
#include "esphome/components/number/number.h"
|
||||
#endif
|
||||
|
||||
namespace esphome::zigbee {
|
||||
|
||||
@@ -67,6 +70,9 @@ class ZigbeeAttribute final : public Component {
|
||||
#endif
|
||||
#ifdef USE_SWITCH
|
||||
template<typename T> void connect(switch_::Switch *device);
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
template<typename T> void connect(number::Number *device);
|
||||
#endif
|
||||
bool report_enabled = false;
|
||||
|
||||
@@ -157,6 +163,25 @@ template<typename T> void ZigbeeAttribute::connect(switch_::Switch *device) {
|
||||
device->add_on_state_callback([this](bool value) { this->set_attr((T) (this->scale_ * value)); });
|
||||
}
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
template<typename T> void ZigbeeAttribute::connect(number::Number *device) {
|
||||
// Add min, max and step attributes to the analog output cluster
|
||||
if (this->cluster_id_ == EZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT) {
|
||||
this->zb_->add_attr(this->endpoint_id_, this->cluster_id_, this->role_, 0x0045, this->max_size_,
|
||||
device->traits.get_min_value());
|
||||
this->zb_->add_attr(this->endpoint_id_, this->cluster_id_, this->role_, 0x0041, this->max_size_,
|
||||
device->traits.get_max_value());
|
||||
this->zb_->add_attr(this->endpoint_id_, this->cluster_id_, this->role_, 0x006A, this->max_size_,
|
||||
device->traits.get_step());
|
||||
}
|
||||
this->add_on_value_callback([this, device](ezb_zcl_attribute_t attribute) {
|
||||
auto call = device->make_call();
|
||||
call.set_value(static_cast<float>(*(T *) attribute.data.value) / this->scale_);
|
||||
call.perform();
|
||||
});
|
||||
device->add_on_state_callback([this](float value) { this->set_attr(this->scale_value_<T>(value)); });
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace esphome::zigbee
|
||||
|
||||
|
||||
@@ -78,6 +78,35 @@ ANALOG_INPUT_EP = {
|
||||
],
|
||||
}
|
||||
|
||||
ANALOG_OUTPUT_EP = {
|
||||
CONF_CLUSTERS: [
|
||||
{
|
||||
CONF_ID: "ANALOG_OUTPUT",
|
||||
ROLE: "SERVER",
|
||||
CONF_ATTRIBUTES: [
|
||||
{
|
||||
CONF_ATTRIBUTE_ID: 0x55,
|
||||
CONF_TYPE: "SINGLE",
|
||||
CONF_REPORT: cv.enum(REPORT, lower=True)("default"),
|
||||
CONNECT: True,
|
||||
},
|
||||
{
|
||||
CONF_ATTRIBUTE_ID: 0x51,
|
||||
CONF_TYPE: "BOOL",
|
||||
},
|
||||
{
|
||||
CONF_ATTRIBUTE_ID: 0x6F,
|
||||
CONF_TYPE: "MAP8",
|
||||
},
|
||||
{
|
||||
CONF_ATTRIBUTE_ID: 0x1C,
|
||||
CONF_TYPE: "STRING",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
BINARY_INPUT_EP = {
|
||||
DEVICE_TYPE: "SIMPLE_SENSOR",
|
||||
CONF_CLUSTERS: [
|
||||
|
||||
@@ -36,7 +36,7 @@ import esphome.final_validate as fv
|
||||
from esphome.types import ConfigType
|
||||
|
||||
from .const import (
|
||||
ANALOG_INPUT_APPTYPE,
|
||||
ANALOG_APPTYPE,
|
||||
BACNET_UNIT_NO_UNITS,
|
||||
BACNET_UNITS,
|
||||
CONF_CLUSTER,
|
||||
@@ -67,6 +67,7 @@ from .const_esp32 import (
|
||||
)
|
||||
from .zigbee_ep_esp32 import (
|
||||
ANALOG_INPUT_EP,
|
||||
ANALOG_OUTPUT_EP,
|
||||
BINARY_INPUT_EP,
|
||||
BINARY_OUTPUT_EP,
|
||||
BINARY_SENSOR_EP_CONFIGS,
|
||||
@@ -229,7 +230,7 @@ def validate_sensor_esp32(config: ConfigType) -> ConfigType:
|
||||
attr[CONF_LAMBDA] = attr[CONF_LAMBDA][unit]
|
||||
else:
|
||||
ep = copy.deepcopy(ANALOG_INPUT_EP)
|
||||
apptype = ANALOG_INPUT_APPTYPE.get((dev_class, unit))
|
||||
apptype = ANALOG_APPTYPE.get((dev_class, unit))
|
||||
bacunit = BACNET_UNITS.get(unit, BACNET_UNIT_NO_UNITS)
|
||||
accuracy = config.get(CONF_ACCURACY_DECIMALS)
|
||||
if apptype is not None:
|
||||
@@ -261,6 +262,34 @@ def validate_sensor_esp32(config: ConfigType) -> ConfigType:
|
||||
return config
|
||||
|
||||
|
||||
def validate_number_esp32(config: ConfigType) -> ConfigType:
|
||||
# get application type from device class and meas unit
|
||||
# if none get BACNET unit from meas unit
|
||||
dev_class = config.get(CONF_DEVICE_CLASS)
|
||||
unit = config.get(CONF_UNIT_OF_MEASUREMENT)
|
||||
ep = copy.deepcopy(ANALOG_OUTPUT_EP)
|
||||
apptype = ANALOG_APPTYPE.get((dev_class, unit))
|
||||
bacunit = BACNET_UNITS.get(unit, BACNET_UNIT_NO_UNITS)
|
||||
if apptype is not None:
|
||||
ep[CONF_CLUSTERS][0][CONF_ATTRIBUTES].append(
|
||||
{
|
||||
CONF_ATTRIBUTE_ID: 0x100,
|
||||
CONF_VALUE: (apptype << 16) | 0x0100FFFF,
|
||||
CONF_TYPE: "UINT32",
|
||||
},
|
||||
)
|
||||
ep[CONF_CLUSTERS][0][CONF_ATTRIBUTES].append(
|
||||
{
|
||||
CONF_ATTRIBUTE_ID: 0x75,
|
||||
CONF_VALUE: bacunit,
|
||||
CONF_TYPE: "ENUM16",
|
||||
},
|
||||
)
|
||||
setup_attributes(config, ep[CONF_CLUSTERS])
|
||||
add_ep(ep, config.get(CONF_ENDPOINT), config.get(CONF_USE_DEVICE_TYPE))
|
||||
return config
|
||||
|
||||
|
||||
def validate_binary_sensor_esp32(config: ConfigType) -> ConfigType:
|
||||
dev_class = config.get(CONF_DEVICE_CLASS)
|
||||
if config[CONF_CLUSTER] == "device_class":
|
||||
|
||||
@@ -72,6 +72,8 @@ ezb_zcl_cluster_desc_t esphome_zb_default_cluster_dscr_create(uint16_t cluster_i
|
||||
return ezb_zcl_time_create_cluster_desc(NULL, role_mask);
|
||||
case EZB_ZCL_CLUSTER_ID_ANALOG_INPUT:
|
||||
return ezb_zcl_analog_input_create_cluster_desc(NULL, role_mask);
|
||||
case EZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT:
|
||||
return ezb_zcl_analog_output_create_cluster_desc(NULL, role_mask);
|
||||
case EZB_ZCL_CLUSTER_ID_BINARY_INPUT:
|
||||
return ezb_zcl_binary_input_create_cluster_desc(NULL, role_mask);
|
||||
case EZB_ZCL_CLUSTER_ID_BINARY_OUTPUT:
|
||||
@@ -113,6 +115,8 @@ ezb_err_t esphome_zb_cluster_add_attr(uint16_t cluster_id, ezb_zcl_cluster_desc_
|
||||
return ezb_zcl_time_cluster_desc_add_attr(cluster_desc, attr_id, value_p);
|
||||
case EZB_ZCL_CLUSTER_ID_ANALOG_INPUT:
|
||||
return ezb_zcl_analog_input_cluster_desc_add_attr(cluster_desc, attr_id, value_p);
|
||||
case EZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT:
|
||||
return ezb_zcl_analog_output_cluster_desc_add_attr(cluster_desc, attr_id, value_p);
|
||||
case EZB_ZCL_CLUSTER_ID_BINARY_INPUT:
|
||||
return ezb_zcl_binary_input_cluster_desc_add_attr(cluster_desc, attr_id, value_p);
|
||||
case EZB_ZCL_CLUSTER_ID_BINARY_OUTPUT:
|
||||
|
||||
@@ -45,6 +45,7 @@ number:
|
||||
max_value: 100
|
||||
step: 1
|
||||
unit_of_measurement: "°C"
|
||||
device_class: temperature
|
||||
|
||||
time:
|
||||
- platform: zigbee
|
||||
|
||||
Reference in New Issue
Block a user