mirror of
https://github.com/esphome/esphome.git
synced 2026-02-08 00:31:58 +00:00
[nrf52,zigbee] Support for number component (#13581)
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
from esphome import automation
|
from esphome import automation
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components import mqtt, web_server
|
from esphome.components import mqtt, web_server, zigbee
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import (
|
from esphome.const import (
|
||||||
CONF_ABOVE,
|
CONF_ABOVE,
|
||||||
@@ -189,6 +189,7 @@ validate_unit_of_measurement = cv.string_strict
|
|||||||
_NUMBER_SCHEMA = (
|
_NUMBER_SCHEMA = (
|
||||||
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
|
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
|
||||||
.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
|
.extend(cv.MQTT_COMMAND_COMPONENT_SCHEMA)
|
||||||
|
.extend(zigbee.NUMBER_SCHEMA)
|
||||||
.extend(
|
.extend(
|
||||||
{
|
{
|
||||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTNumberComponent),
|
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTNumberComponent),
|
||||||
@@ -214,6 +215,7 @@ _NUMBER_SCHEMA = (
|
|||||||
|
|
||||||
|
|
||||||
_NUMBER_SCHEMA.add_extra(entity_duplicate_validator("number"))
|
_NUMBER_SCHEMA.add_extra(entity_duplicate_validator("number"))
|
||||||
|
_NUMBER_SCHEMA.add_extra(zigbee.validate_number)
|
||||||
|
|
||||||
|
|
||||||
def number_schema(
|
def number_schema(
|
||||||
@@ -277,6 +279,8 @@ async def setup_number_core_(
|
|||||||
if web_server_config := config.get(CONF_WEB_SERVER):
|
if web_server_config := config.get(CONF_WEB_SERVER):
|
||||||
await web_server.add_entity_config(var, web_server_config)
|
await web_server.add_entity_config(var, web_server_config)
|
||||||
|
|
||||||
|
await zigbee.setup_number(var, config, min_value, max_value, step)
|
||||||
|
|
||||||
|
|
||||||
async def register_number(
|
async def register_number(
|
||||||
var, config, *, min_value: float, max_value: float, step: float
|
var, config, *, min_value: float, max_value: float, step: float
|
||||||
|
|||||||
@@ -24,7 +24,12 @@ from .const_zephyr import (
|
|||||||
ZigbeeComponent,
|
ZigbeeComponent,
|
||||||
zigbee_ns,
|
zigbee_ns,
|
||||||
)
|
)
|
||||||
from .zigbee_zephyr import zephyr_binary_sensor, zephyr_sensor, zephyr_switch
|
from .zigbee_zephyr import (
|
||||||
|
zephyr_binary_sensor,
|
||||||
|
zephyr_number,
|
||||||
|
zephyr_sensor,
|
||||||
|
zephyr_switch,
|
||||||
|
)
|
||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -43,6 +48,7 @@ def zigbee_set_core_data(config: ConfigType) -> ConfigType:
|
|||||||
BINARY_SENSOR_SCHEMA = cv.Schema({}).extend(zephyr_binary_sensor)
|
BINARY_SENSOR_SCHEMA = cv.Schema({}).extend(zephyr_binary_sensor)
|
||||||
SENSOR_SCHEMA = cv.Schema({}).extend(zephyr_sensor)
|
SENSOR_SCHEMA = cv.Schema({}).extend(zephyr_sensor)
|
||||||
SWITCH_SCHEMA = cv.Schema({}).extend(zephyr_switch)
|
SWITCH_SCHEMA = cv.Schema({}).extend(zephyr_switch)
|
||||||
|
NUMBER_SCHEMA = cv.Schema({}).extend(zephyr_number)
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
cv.Schema(
|
cv.Schema(
|
||||||
@@ -125,6 +131,21 @@ async def setup_switch(entity: cg.MockObj, config: ConfigType) -> None:
|
|||||||
await zephyr_setup_switch(entity, config)
|
await zephyr_setup_switch(entity, config)
|
||||||
|
|
||||||
|
|
||||||
|
async def setup_number(
|
||||||
|
entity: cg.MockObj,
|
||||||
|
config: ConfigType,
|
||||||
|
min_value: float,
|
||||||
|
max_value: float,
|
||||||
|
step: float,
|
||||||
|
) -> None:
|
||||||
|
if not config.get(CONF_ZIGBEE_ID) or config.get(CONF_INTERNAL):
|
||||||
|
return
|
||||||
|
if CORE.using_zephyr:
|
||||||
|
from .zigbee_zephyr import zephyr_setup_number
|
||||||
|
|
||||||
|
await zephyr_setup_number(entity, config, min_value, max_value, step)
|
||||||
|
|
||||||
|
|
||||||
def consume_endpoint(config: ConfigType) -> ConfigType:
|
def consume_endpoint(config: ConfigType) -> ConfigType:
|
||||||
if not config.get(CONF_ZIGBEE_ID) or config.get(CONF_INTERNAL):
|
if not config.get(CONF_ZIGBEE_ID) or config.get(CONF_INTERNAL):
|
||||||
return config
|
return config
|
||||||
@@ -152,6 +173,10 @@ def validate_switch(config: ConfigType) -> ConfigType:
|
|||||||
return consume_endpoint(config)
|
return consume_endpoint(config)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_number(config: ConfigType) -> ConfigType:
|
||||||
|
return consume_endpoint(config)
|
||||||
|
|
||||||
|
|
||||||
ZIGBEE_ACTION_SCHEMA = automation.maybe_simple_id(
|
ZIGBEE_ACTION_SCHEMA = automation.maybe_simple_id(
|
||||||
cv.Schema(
|
cv.Schema(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ zigbee_ns = cg.esphome_ns.namespace("zigbee")
|
|||||||
ZigbeeComponent = zigbee_ns.class_("ZigbeeComponent", cg.Component)
|
ZigbeeComponent = zigbee_ns.class_("ZigbeeComponent", cg.Component)
|
||||||
BinaryAttrs = zigbee_ns.struct("BinaryAttrs")
|
BinaryAttrs = zigbee_ns.struct("BinaryAttrs")
|
||||||
AnalogAttrs = zigbee_ns.struct("AnalogAttrs")
|
AnalogAttrs = zigbee_ns.struct("AnalogAttrs")
|
||||||
|
AnalogAttrsOutput = zigbee_ns.struct("AnalogAttrsOutput")
|
||||||
|
|
||||||
CONF_MAX_EP_NUMBER = 8
|
CONF_MAX_EP_NUMBER = 8
|
||||||
CONF_ZIGBEE_ID = "zigbee_id"
|
CONF_ZIGBEE_ID = "zigbee_id"
|
||||||
@@ -12,6 +13,7 @@ CONF_WIPE_ON_BOOT = "wipe_on_boot"
|
|||||||
CONF_ZIGBEE_BINARY_SENSOR = "zigbee_binary_sensor"
|
CONF_ZIGBEE_BINARY_SENSOR = "zigbee_binary_sensor"
|
||||||
CONF_ZIGBEE_SENSOR = "zigbee_sensor"
|
CONF_ZIGBEE_SENSOR = "zigbee_sensor"
|
||||||
CONF_ZIGBEE_SWITCH = "zigbee_switch"
|
CONF_ZIGBEE_SWITCH = "zigbee_switch"
|
||||||
|
CONF_ZIGBEE_NUMBER = "zigbee_number"
|
||||||
CONF_POWER_SOURCE = "power_source"
|
CONF_POWER_SOURCE = "power_source"
|
||||||
POWER_SOURCE = {
|
POWER_SOURCE = {
|
||||||
"UNKNOWN": "ZB_ZCL_BASIC_POWER_SOURCE_UNKNOWN",
|
"UNKNOWN": "ZB_ZCL_BASIC_POWER_SOURCE_UNKNOWN",
|
||||||
@@ -38,3 +40,4 @@ ZB_ZCL_CLUSTER_ID_IDENTIFY = "ZB_ZCL_CLUSTER_ID_IDENTIFY"
|
|||||||
ZB_ZCL_CLUSTER_ID_BINARY_INPUT = "ZB_ZCL_CLUSTER_ID_BINARY_INPUT"
|
ZB_ZCL_CLUSTER_ID_BINARY_INPUT = "ZB_ZCL_CLUSTER_ID_BINARY_INPUT"
|
||||||
ZB_ZCL_CLUSTER_ID_ANALOG_INPUT = "ZB_ZCL_CLUSTER_ID_ANALOG_INPUT"
|
ZB_ZCL_CLUSTER_ID_ANALOG_INPUT = "ZB_ZCL_CLUSTER_ID_ANALOG_INPUT"
|
||||||
ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT = "ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT"
|
ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT = "ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT"
|
||||||
|
ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT = "ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT"
|
||||||
|
|||||||
111
esphome/components/zigbee/zigbee_number_zephyr.cpp
Normal file
111
esphome/components/zigbee/zigbee_number_zephyr.cpp
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
#include "zigbee_number_zephyr.h"
|
||||||
|
#if defined(USE_ZIGBEE) && defined(USE_NRF52) && defined(USE_NUMBER)
|
||||||
|
#include "esphome/core/log.h"
|
||||||
|
extern "C" {
|
||||||
|
#include <zboss_api.h>
|
||||||
|
#include <zboss_api_addons.h>
|
||||||
|
#include <zb_nrf_platform.h>
|
||||||
|
#include <zigbee/zigbee_app_utils.h>
|
||||||
|
#include <zb_error_to_string.h>
|
||||||
|
}
|
||||||
|
namespace esphome::zigbee {
|
||||||
|
|
||||||
|
static const char *const TAG = "zigbee.number";
|
||||||
|
|
||||||
|
void ZigbeeNumber::setup() {
|
||||||
|
this->parent_->add_callback(this->endpoint_, [this](zb_bufid_t bufid) { this->zcl_device_cb_(bufid); });
|
||||||
|
this->number_->add_on_state_callback([this](float state) {
|
||||||
|
this->cluster_attributes_->present_value = state;
|
||||||
|
ESP_LOGD(TAG, "Set attribute endpoint: %d, present_value %f", this->endpoint_,
|
||||||
|
this->cluster_attributes_->present_value);
|
||||||
|
ZB_ZCL_SET_ATTRIBUTE(this->endpoint_, ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT, ZB_ZCL_CLUSTER_SERVER_ROLE,
|
||||||
|
ZB_ZCL_ATTR_ANALOG_OUTPUT_PRESENT_VALUE_ID, (zb_uint8_t *) &cluster_attributes_->present_value,
|
||||||
|
ZB_FALSE);
|
||||||
|
this->parent_->force_report();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ZigbeeNumber::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG,
|
||||||
|
"Zigbee Number\n"
|
||||||
|
" Endpoint: %d, present_value %f",
|
||||||
|
this->endpoint_, this->cluster_attributes_->present_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ZigbeeNumber::zcl_device_cb_(zb_bufid_t bufid) {
|
||||||
|
zb_zcl_device_callback_param_t *p_device_cb_param = ZB_BUF_GET_PARAM(bufid, zb_zcl_device_callback_param_t);
|
||||||
|
zb_zcl_device_callback_id_t device_cb_id = p_device_cb_param->device_cb_id;
|
||||||
|
zb_uint16_t cluster_id = p_device_cb_param->cb_param.set_attr_value_param.cluster_id;
|
||||||
|
zb_uint16_t attr_id = p_device_cb_param->cb_param.set_attr_value_param.attr_id;
|
||||||
|
|
||||||
|
switch (device_cb_id) {
|
||||||
|
/* ZCL set attribute value */
|
||||||
|
case ZB_ZCL_SET_ATTR_VALUE_CB_ID:
|
||||||
|
if (cluster_id == ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT) {
|
||||||
|
ESP_LOGI(TAG, "Analog output attribute setting");
|
||||||
|
if (attr_id == ZB_ZCL_ATTR_ANALOG_OUTPUT_PRESENT_VALUE_ID) {
|
||||||
|
float value =
|
||||||
|
*reinterpret_cast<const float *>(&p_device_cb_param->cb_param.set_attr_value_param.values.data32);
|
||||||
|
this->defer([this, value]() {
|
||||||
|
this->cluster_attributes_->present_value = value;
|
||||||
|
auto call = this->number_->make_call();
|
||||||
|
call.set_value(value);
|
||||||
|
call.perform();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* other clusters attribute handled here */
|
||||||
|
ESP_LOGI(TAG, "Unhandled cluster attribute id: %d", cluster_id);
|
||||||
|
p_device_cb_param->status = RET_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
p_device_cb_param->status = RET_NOT_IMPLEMENTED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "%s status: %hd", __func__, p_device_cb_param->status);
|
||||||
|
}
|
||||||
|
|
||||||
|
const zb_uint8_t ZB_ZCL_ANALOG_OUTPUT_STATUS_FLAG_MAX_VALUE = 0x0F;
|
||||||
|
|
||||||
|
static zb_ret_t check_value_analog_server(zb_uint16_t attr_id, zb_uint8_t endpoint,
|
||||||
|
zb_uint8_t *value) { // NOLINT(readability-non-const-parameter)
|
||||||
|
zb_ret_t ret = RET_OK;
|
||||||
|
ZVUNUSED(endpoint);
|
||||||
|
|
||||||
|
switch (attr_id) {
|
||||||
|
case ZB_ZCL_ATTR_ANALOG_OUTPUT_OUT_OF_SERVICE_ID:
|
||||||
|
ret = ZB_ZCL_CHECK_BOOL_VALUE(*value) ? RET_OK : RET_ERROR;
|
||||||
|
break;
|
||||||
|
case ZB_ZCL_ATTR_ANALOG_OUTPUT_PRESENT_VALUE_ID:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ZB_ZCL_ATTR_ANALOG_OUTPUT_STATUS_FLAG_ID:
|
||||||
|
if (*value > ZB_ZCL_ANALOG_OUTPUT_STATUS_FLAG_MAX_VALUE) {
|
||||||
|
ret = RET_ERROR;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace esphome::zigbee
|
||||||
|
|
||||||
|
void zb_zcl_analog_output_init_server() {
|
||||||
|
zb_zcl_add_cluster_handlers(ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT, ZB_ZCL_CLUSTER_SERVER_ROLE,
|
||||||
|
esphome::zigbee::check_value_analog_server, (zb_zcl_cluster_write_attr_hook_t) NULL,
|
||||||
|
(zb_zcl_cluster_handler_t) NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void zb_zcl_analog_output_init_client() {
|
||||||
|
zb_zcl_add_cluster_handlers(ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT, ZB_ZCL_CLUSTER_CLIENT_ROLE,
|
||||||
|
(zb_zcl_cluster_check_value_t) NULL, (zb_zcl_cluster_write_attr_hook_t) NULL,
|
||||||
|
(zb_zcl_cluster_handler_t) NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
118
esphome/components/zigbee/zigbee_number_zephyr.h
Normal file
118
esphome/components/zigbee/zigbee_number_zephyr.h
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/core/defines.h"
|
||||||
|
#if defined(USE_ZIGBEE) && defined(USE_NRF52) && defined(USE_NUMBER)
|
||||||
|
#include "esphome/components/zigbee/zigbee_zephyr.h"
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/number/number.h"
|
||||||
|
extern "C" {
|
||||||
|
#include <zboss_api.h>
|
||||||
|
#include <zboss_api_addons.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ZB_ZCL_ATTR_ANALOG_OUTPUT_DESCRIPTION_ID = 0x001C,
|
||||||
|
ZB_ZCL_ATTR_ANALOG_OUTPUT_MAX_PRESENT_VALUE_ID = 0x0041,
|
||||||
|
ZB_ZCL_ATTR_ANALOG_OUTPUT_MIN_PRESENT_VALUE_ID = 0x0045,
|
||||||
|
ZB_ZCL_ATTR_ANALOG_OUTPUT_OUT_OF_SERVICE_ID = 0x0051,
|
||||||
|
ZB_ZCL_ATTR_ANALOG_OUTPUT_PRESENT_VALUE_ID = 0x0055,
|
||||||
|
ZB_ZCL_ATTR_ANALOG_OUTPUT_RESOLUTION_ID = 0x006A,
|
||||||
|
ZB_ZCL_ATTR_ANALOG_OUTPUT_STATUS_FLAG_ID = 0x006F,
|
||||||
|
ZB_ZCL_ATTR_ANALOG_OUTPUT_ENGINEERING_UNITS_ID = 0x0075,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define ZB_ZCL_ANALOG_OUTPUT_CLUSTER_REVISION_DEFAULT ((zb_uint16_t) 0x0001u)
|
||||||
|
|
||||||
|
#define ZB_SET_ATTR_DESCR_WITH_ZB_ZCL_ATTR_ANALOG_OUTPUT_DESCRIPTION_ID(data_ptr) \
|
||||||
|
{ \
|
||||||
|
ZB_ZCL_ATTR_ANALOG_OUTPUT_DESCRIPTION_ID, ZB_ZCL_ATTR_TYPE_CHAR_STRING, ZB_ZCL_ATTR_ACCESS_READ_ONLY, \
|
||||||
|
(ZB_ZCL_NON_MANUFACTURER_SPECIFIC), (void *) (data_ptr) \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ZB_SET_ATTR_DESCR_WITH_ZB_ZCL_ATTR_ANALOG_OUTPUT_OUT_OF_SERVICE_ID(data_ptr) \
|
||||||
|
{ \
|
||||||
|
ZB_ZCL_ATTR_ANALOG_OUTPUT_OUT_OF_SERVICE_ID, ZB_ZCL_ATTR_TYPE_BOOL, \
|
||||||
|
ZB_ZCL_ATTR_ACCESS_READ_ONLY | ZB_ZCL_ATTR_ACCESS_WRITE_OPTIONAL, (ZB_ZCL_NON_MANUFACTURER_SPECIFIC), \
|
||||||
|
(void *) (data_ptr) \
|
||||||
|
}
|
||||||
|
// PresentValue
|
||||||
|
#define ZB_SET_ATTR_DESCR_WITH_ZB_ZCL_ATTR_ANALOG_OUTPUT_PRESENT_VALUE_ID(data_ptr) \
|
||||||
|
{ \
|
||||||
|
ZB_ZCL_ATTR_ANALOG_OUTPUT_PRESENT_VALUE_ID, ZB_ZCL_ATTR_TYPE_SINGLE, \
|
||||||
|
ZB_ZCL_ATTR_ACCESS_READ_WRITE | ZB_ZCL_ATTR_ACCESS_REPORTING, (ZB_ZCL_NON_MANUFACTURER_SPECIFIC), \
|
||||||
|
(void *) (data_ptr) \
|
||||||
|
}
|
||||||
|
// MaxPresentValue
|
||||||
|
#define ZB_SET_ATTR_DESCR_WITH_ZB_ZCL_ATTR_ANALOG_OUTPUT_MAX_PRESENT_VALUE_ID(data_ptr) \
|
||||||
|
{ \
|
||||||
|
ZB_ZCL_ATTR_ANALOG_OUTPUT_MAX_PRESENT_VALUE_ID, ZB_ZCL_ATTR_TYPE_SINGLE, \
|
||||||
|
ZB_ZCL_ATTR_ACCESS_READ_ONLY | ZB_ZCL_ATTR_ACCESS_WRITE_OPTIONAL, (ZB_ZCL_NON_MANUFACTURER_SPECIFIC), \
|
||||||
|
(void *) (data_ptr) \
|
||||||
|
}
|
||||||
|
// MinPresentValue
|
||||||
|
#define ZB_SET_ATTR_DESCR_WITH_ZB_ZCL_ATTR_ANALOG_OUTPUT_MIN_PRESENT_VALUE_ID(data_ptr) \
|
||||||
|
{ \
|
||||||
|
ZB_ZCL_ATTR_ANALOG_OUTPUT_MIN_PRESENT_VALUE_ID, ZB_ZCL_ATTR_TYPE_SINGLE, \
|
||||||
|
ZB_ZCL_ATTR_ACCESS_READ_ONLY | ZB_ZCL_ATTR_ACCESS_WRITE_OPTIONAL, (ZB_ZCL_NON_MANUFACTURER_SPECIFIC), \
|
||||||
|
(void *) (data_ptr) \
|
||||||
|
}
|
||||||
|
// Resolution
|
||||||
|
#define ZB_SET_ATTR_DESCR_WITH_ZB_ZCL_ATTR_ANALOG_OUTPUT_RESOLUTION_ID(data_ptr) \
|
||||||
|
{ \
|
||||||
|
ZB_ZCL_ATTR_ANALOG_OUTPUT_RESOLUTION_ID, ZB_ZCL_ATTR_TYPE_SINGLE, \
|
||||||
|
ZB_ZCL_ATTR_ACCESS_READ_ONLY | ZB_ZCL_ATTR_ACCESS_WRITE_OPTIONAL, (ZB_ZCL_NON_MANUFACTURER_SPECIFIC), \
|
||||||
|
(void *) (data_ptr) \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ZB_SET_ATTR_DESCR_WITH_ZB_ZCL_ATTR_ANALOG_OUTPUT_STATUS_FLAG_ID(data_ptr) \
|
||||||
|
{ \
|
||||||
|
ZB_ZCL_ATTR_ANALOG_OUTPUT_STATUS_FLAG_ID, ZB_ZCL_ATTR_TYPE_8BITMAP, \
|
||||||
|
ZB_ZCL_ATTR_ACCESS_READ_ONLY | ZB_ZCL_ATTR_ACCESS_REPORTING, (ZB_ZCL_NON_MANUFACTURER_SPECIFIC), \
|
||||||
|
(void *) (data_ptr) \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ZB_SET_ATTR_DESCR_WITH_ZB_ZCL_ATTR_ANALOG_OUTPUT_ENGINEERING_UNITS_ID(data_ptr) \
|
||||||
|
{ \
|
||||||
|
ZB_ZCL_ATTR_ANALOG_OUTPUT_ENGINEERING_UNITS_ID, ZB_ZCL_ATTR_TYPE_16BIT_ENUM, ZB_ZCL_ATTR_ACCESS_READ_ONLY, \
|
||||||
|
(ZB_ZCL_NON_MANUFACTURER_SPECIFIC), (void *) (data_ptr) \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ESPHOME_ZB_ZCL_DECLARE_ANALOG_OUTPUT_ATTRIB_LIST(attr_list, out_of_service, present_value, status_flag, \
|
||||||
|
max_present_value, min_present_value, resolution, \
|
||||||
|
engineering_units, description) \
|
||||||
|
ZB_ZCL_START_DECLARE_ATTRIB_LIST_CLUSTER_REVISION(attr_list, ZB_ZCL_ANALOG_OUTPUT) \
|
||||||
|
ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_ANALOG_OUTPUT_OUT_OF_SERVICE_ID, (out_of_service)) \
|
||||||
|
ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_ANALOG_OUTPUT_PRESENT_VALUE_ID, (present_value)) \
|
||||||
|
ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_ANALOG_OUTPUT_STATUS_FLAG_ID, (status_flag)) \
|
||||||
|
ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_ANALOG_OUTPUT_MAX_PRESENT_VALUE_ID, (max_present_value)) \
|
||||||
|
ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_ANALOG_OUTPUT_MIN_PRESENT_VALUE_ID, (min_present_value)) \
|
||||||
|
ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_ANALOG_OUTPUT_RESOLUTION_ID, (resolution)) \
|
||||||
|
ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_ANALOG_OUTPUT_ENGINEERING_UNITS_ID, (engineering_units)) \
|
||||||
|
ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_ANALOG_OUTPUT_DESCRIPTION_ID, (description)) \
|
||||||
|
ZB_ZCL_FINISH_DECLARE_ATTRIB_LIST
|
||||||
|
|
||||||
|
void zb_zcl_analog_output_init_server();
|
||||||
|
void zb_zcl_analog_output_init_client();
|
||||||
|
#define ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT_SERVER_ROLE_INIT zb_zcl_analog_output_init_server
|
||||||
|
#define ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT_CLIENT_ROLE_INIT zb_zcl_analog_output_init_client
|
||||||
|
|
||||||
|
namespace esphome::zigbee {
|
||||||
|
|
||||||
|
class ZigbeeNumber : public ZigbeeEntity, public Component {
|
||||||
|
public:
|
||||||
|
ZigbeeNumber(number::Number *n) : number_(n) {}
|
||||||
|
void set_cluster_attributes(AnalogAttrsOutput &cluster_attributes) {
|
||||||
|
this->cluster_attributes_ = &cluster_attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() override;
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
number::Number *number_;
|
||||||
|
AnalogAttrsOutput *cluster_attributes_{nullptr};
|
||||||
|
void zcl_device_cb_(zb_bufid_t bufid);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace esphome::zigbee
|
||||||
|
#endif
|
||||||
@@ -50,7 +50,7 @@ void ZigbeeSwitch::zcl_device_cb_(zb_bufid_t bufid) {
|
|||||||
if (attr_id == ZB_ZCL_ATTR_BINARY_OUTPUT_PRESENT_VALUE_ID) {
|
if (attr_id == ZB_ZCL_ATTR_BINARY_OUTPUT_PRESENT_VALUE_ID) {
|
||||||
this->defer([this, value]() {
|
this->defer([this, value]() {
|
||||||
this->cluster_attributes_->present_value = value ? ZB_TRUE : ZB_FALSE;
|
this->cluster_attributes_->present_value = value ? ZB_TRUE : ZB_FALSE;
|
||||||
this->switch_->publish_state(value);
|
this->switch_->control(value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -101,8 +101,8 @@ void ZigbeeComponent::zcl_device_cb(zb_bufid_t bufid) {
|
|||||||
zb_uint16_t attr_id = p_device_cb_param->cb_param.set_attr_value_param.attr_id;
|
zb_uint16_t attr_id = p_device_cb_param->cb_param.set_attr_value_param.attr_id;
|
||||||
auto endpoint = p_device_cb_param->endpoint;
|
auto endpoint = p_device_cb_param->endpoint;
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Zcl_device_cb %s id %hd, cluster_id %d, attr_id %d, endpoint: %d", __func__, device_cb_id, cluster_id,
|
ESP_LOGI(TAG, "%s id %hd, cluster_id %d, attr_id %d, endpoint: %d", __func__, device_cb_id, cluster_id, attr_id,
|
||||||
attr_id, endpoint);
|
endpoint);
|
||||||
|
|
||||||
/* Set default response value. */
|
/* Set default response value. */
|
||||||
p_device_cb_param->status = RET_OK;
|
p_device_cb_param->status = RET_OK;
|
||||||
|
|||||||
@@ -60,6 +60,12 @@ struct AnalogAttrs {
|
|||||||
zb_uchar_t description[ZB_ZCL_MAX_STRING_SIZE];
|
zb_uchar_t description[ZB_ZCL_MAX_STRING_SIZE];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct AnalogAttrsOutput : AnalogAttrs {
|
||||||
|
float max_present_value;
|
||||||
|
float min_present_value;
|
||||||
|
float resolution;
|
||||||
|
};
|
||||||
|
|
||||||
class ZigbeeComponent : public Component {
|
class ZigbeeComponent : public Component {
|
||||||
public:
|
public:
|
||||||
void setup() override;
|
void setup() override;
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ from .const_zephyr import (
|
|||||||
CONF_WIPE_ON_BOOT,
|
CONF_WIPE_ON_BOOT,
|
||||||
CONF_ZIGBEE_BINARY_SENSOR,
|
CONF_ZIGBEE_BINARY_SENSOR,
|
||||||
CONF_ZIGBEE_ID,
|
CONF_ZIGBEE_ID,
|
||||||
|
CONF_ZIGBEE_NUMBER,
|
||||||
CONF_ZIGBEE_SENSOR,
|
CONF_ZIGBEE_SENSOR,
|
||||||
CONF_ZIGBEE_SWITCH,
|
CONF_ZIGBEE_SWITCH,
|
||||||
KEY_EP_NUMBER,
|
KEY_EP_NUMBER,
|
||||||
@@ -62,12 +63,14 @@ from .const_zephyr import (
|
|||||||
POWER_SOURCE,
|
POWER_SOURCE,
|
||||||
ZB_ZCL_BASIC_ATTRS_EXT_T,
|
ZB_ZCL_BASIC_ATTRS_EXT_T,
|
||||||
ZB_ZCL_CLUSTER_ID_ANALOG_INPUT,
|
ZB_ZCL_CLUSTER_ID_ANALOG_INPUT,
|
||||||
|
ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT,
|
||||||
ZB_ZCL_CLUSTER_ID_BASIC,
|
ZB_ZCL_CLUSTER_ID_BASIC,
|
||||||
ZB_ZCL_CLUSTER_ID_BINARY_INPUT,
|
ZB_ZCL_CLUSTER_ID_BINARY_INPUT,
|
||||||
ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT,
|
ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT,
|
||||||
ZB_ZCL_CLUSTER_ID_IDENTIFY,
|
ZB_ZCL_CLUSTER_ID_IDENTIFY,
|
||||||
ZB_ZCL_IDENTIFY_ATTRS_T,
|
ZB_ZCL_IDENTIFY_ATTRS_T,
|
||||||
AnalogAttrs,
|
AnalogAttrs,
|
||||||
|
AnalogAttrsOutput,
|
||||||
BinaryAttrs,
|
BinaryAttrs,
|
||||||
ZigbeeComponent,
|
ZigbeeComponent,
|
||||||
zigbee_ns,
|
zigbee_ns,
|
||||||
@@ -76,6 +79,7 @@ from .const_zephyr import (
|
|||||||
ZigbeeBinarySensor = zigbee_ns.class_("ZigbeeBinarySensor", cg.Component)
|
ZigbeeBinarySensor = zigbee_ns.class_("ZigbeeBinarySensor", cg.Component)
|
||||||
ZigbeeSensor = zigbee_ns.class_("ZigbeeSensor", cg.Component)
|
ZigbeeSensor = zigbee_ns.class_("ZigbeeSensor", cg.Component)
|
||||||
ZigbeeSwitch = zigbee_ns.class_("ZigbeeSwitch", cg.Component)
|
ZigbeeSwitch = zigbee_ns.class_("ZigbeeSwitch", cg.Component)
|
||||||
|
ZigbeeNumber = zigbee_ns.class_("ZigbeeNumber", cg.Component)
|
||||||
|
|
||||||
# BACnet engineering units mapping (ZCL uses BACnet unit codes)
|
# BACnet engineering units mapping (ZCL uses BACnet unit codes)
|
||||||
# See: https://github.com/zigpy/zha/blob/dev/zha/application/platforms/number/bacnet.py
|
# See: https://github.com/zigpy/zha/blob/dev/zha/application/platforms/number/bacnet.py
|
||||||
@@ -139,6 +143,15 @@ zephyr_switch = cv.Schema(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
zephyr_number = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.OnlyWith(CONF_ZIGBEE_ID, ["nrf52", "zigbee"]): cv.use_id(ZigbeeComponent),
|
||||||
|
cv.OnlyWith(CONF_ZIGBEE_NUMBER, ["nrf52", "zigbee"]): cv.declare_id(
|
||||||
|
ZigbeeNumber
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def zephyr_to_code(config: ConfigType) -> None:
|
async def zephyr_to_code(config: ConfigType) -> None:
|
||||||
zephyr_add_prj_conf("ZIGBEE", True)
|
zephyr_add_prj_conf("ZIGBEE", True)
|
||||||
@@ -344,6 +357,16 @@ async def zephyr_setup_switch(entity: cg.MockObj, config: ConfigType) -> None:
|
|||||||
CORE.add_job(_add_switch, entity, config)
|
CORE.add_job(_add_switch, entity, config)
|
||||||
|
|
||||||
|
|
||||||
|
async def zephyr_setup_number(
|
||||||
|
entity: cg.MockObj,
|
||||||
|
config: ConfigType,
|
||||||
|
min_value: float,
|
||||||
|
max_value: float,
|
||||||
|
step: float,
|
||||||
|
) -> None:
|
||||||
|
CORE.add_job(_add_number, entity, config, min_value, max_value, step)
|
||||||
|
|
||||||
|
|
||||||
def get_slot_index() -> int:
|
def get_slot_index() -> int:
|
||||||
"""Find the next available endpoint slot."""
|
"""Find the next available endpoint slot."""
|
||||||
slot = next(
|
slot = next(
|
||||||
@@ -451,3 +474,31 @@ async def _add_switch(entity: cg.MockObj, config: ConfigType) -> None:
|
|||||||
ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT,
|
ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT,
|
||||||
"ZB_HA_CUSTOM_ATTR_DEVICE_ID",
|
"ZB_HA_CUSTOM_ATTR_DEVICE_ID",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def _add_number(
|
||||||
|
entity: cg.MockObj,
|
||||||
|
config: ConfigType,
|
||||||
|
min_value: float,
|
||||||
|
max_value: float,
|
||||||
|
step: float,
|
||||||
|
) -> None:
|
||||||
|
# Get BACnet engineering unit from unit_of_measurement
|
||||||
|
unit = config.get(CONF_UNIT_OF_MEASUREMENT, "")
|
||||||
|
bacnet_unit = BACNET_UNITS.get(unit, BACNET_UNIT_NO_UNITS)
|
||||||
|
|
||||||
|
await _add_zigbee_ep(
|
||||||
|
entity,
|
||||||
|
config,
|
||||||
|
CONF_ZIGBEE_NUMBER,
|
||||||
|
AnalogAttrsOutput,
|
||||||
|
"ESPHOME_ZB_ZCL_DECLARE_ANALOG_OUTPUT_ATTRIB_LIST",
|
||||||
|
ZB_ZCL_CLUSTER_ID_ANALOG_OUTPUT,
|
||||||
|
"ZB_HA_CUSTOM_ATTR_DEVICE_ID",
|
||||||
|
extra_field_values={
|
||||||
|
"max_present_value": max_value,
|
||||||
|
"min_present_value": min_value,
|
||||||
|
"resolution": step,
|
||||||
|
"engineering_units": bacnet_unit,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ binary_sensor:
|
|||||||
name: "Garage Door Open 2"
|
name: "Garage Door Open 2"
|
||||||
- platform: template
|
- platform: template
|
||||||
name: "Garage Door Open 3"
|
name: "Garage Door Open 3"
|
||||||
- platform: template
|
|
||||||
name: "Garage Door Open 4"
|
|
||||||
- platform: template
|
- platform: template
|
||||||
name: "Garage Door Internal"
|
name: "Garage Door Internal"
|
||||||
internal: True
|
internal: True
|
||||||
@@ -44,3 +42,11 @@ switch:
|
|||||||
- platform: template
|
- platform: template
|
||||||
name: "Template Switch"
|
name: "Template Switch"
|
||||||
optimistic: true
|
optimistic: true
|
||||||
|
|
||||||
|
number:
|
||||||
|
- platform: template
|
||||||
|
name: "Template number"
|
||||||
|
optimistic: true
|
||||||
|
min_value: 2
|
||||||
|
max_value: 100
|
||||||
|
step: 1
|
||||||
|
|||||||
Reference in New Issue
Block a user