mirror of
https://github.com/esphome/esphome.git
synced 2026-10-07 03:16:37 +00:00
[nrf52, zigbee] Add sensor (#12187)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
This commit is contained in:
co-authored by
Jonathan Swoboda
parent
d6554702d8
commit
ada4e6d5e9
@@ -3,7 +3,7 @@ import math
|
|||||||
|
|
||||||
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,
|
||||||
@@ -295,6 +295,7 @@ validate_device_class = cv.one_of(*DEVICE_CLASSES, lower=True, space="_")
|
|||||||
_SENSOR_SCHEMA = (
|
_SENSOR_SCHEMA = (
|
||||||
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
|
cv.ENTITY_BASE_SCHEMA.extend(web_server.WEBSERVER_SORTING_SCHEMA)
|
||||||
.extend(cv.MQTT_COMPONENT_SCHEMA)
|
.extend(cv.MQTT_COMPONENT_SCHEMA)
|
||||||
|
.extend(zigbee.SENSOR_SCHEMA)
|
||||||
.extend(
|
.extend(
|
||||||
{
|
{
|
||||||
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTSensorComponent),
|
cv.OnlyWith(CONF_MQTT_ID, "mqtt"): cv.declare_id(mqtt.MQTTSensorComponent),
|
||||||
@@ -335,6 +336,7 @@ _SENSOR_SCHEMA = (
|
|||||||
)
|
)
|
||||||
|
|
||||||
_SENSOR_SCHEMA.add_extra(entity_duplicate_validator("sensor"))
|
_SENSOR_SCHEMA.add_extra(entity_duplicate_validator("sensor"))
|
||||||
|
_SENSOR_SCHEMA.add_extra(zigbee.validate_sensor)
|
||||||
|
|
||||||
|
|
||||||
def sensor_schema(
|
def sensor_schema(
|
||||||
@@ -918,6 +920,8 @@ async def setup_sensor_core_(var, config):
|
|||||||
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_sensor(var, config)
|
||||||
|
|
||||||
|
|
||||||
async def register_sensor(var, config):
|
async def register_sensor(var, config):
|
||||||
if not CORE.has_id(config[CONF_ID]):
|
if not CORE.has_id(config[CONF_ID]):
|
||||||
|
|||||||
@@ -13,14 +13,16 @@ from esphome.types import ConfigType
|
|||||||
from .const_zephyr import (
|
from .const_zephyr import (
|
||||||
CONF_MAX_EP_NUMBER,
|
CONF_MAX_EP_NUMBER,
|
||||||
CONF_ON_JOIN,
|
CONF_ON_JOIN,
|
||||||
|
CONF_POWER_SOURCE,
|
||||||
CONF_WIPE_ON_BOOT,
|
CONF_WIPE_ON_BOOT,
|
||||||
CONF_ZIGBEE_ID,
|
CONF_ZIGBEE_ID,
|
||||||
KEY_EP_NUMBER,
|
KEY_EP_NUMBER,
|
||||||
KEY_ZIGBEE,
|
KEY_ZIGBEE,
|
||||||
|
POWER_SOURCE,
|
||||||
ZigbeeComponent,
|
ZigbeeComponent,
|
||||||
zigbee_ns,
|
zigbee_ns,
|
||||||
)
|
)
|
||||||
from .zigbee_zephyr import zephyr_binary_sensor
|
from .zigbee_zephyr import zephyr_binary_sensor, zephyr_sensor
|
||||||
|
|
||||||
CODEOWNERS = ["@tomaszduda23"]
|
CODEOWNERS = ["@tomaszduda23"]
|
||||||
|
|
||||||
@@ -35,6 +37,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)
|
||||||
|
|
||||||
CONFIG_SCHEMA = cv.All(
|
CONFIG_SCHEMA = cv.All(
|
||||||
cv.Schema(
|
cv.Schema(
|
||||||
@@ -42,9 +45,15 @@ CONFIG_SCHEMA = cv.All(
|
|||||||
cv.GenerateID(CONF_ID): cv.declare_id(ZigbeeComponent),
|
cv.GenerateID(CONF_ID): cv.declare_id(ZigbeeComponent),
|
||||||
cv.Optional(CONF_ON_JOIN): automation.validate_automation(single=True),
|
cv.Optional(CONF_ON_JOIN): automation.validate_automation(single=True),
|
||||||
cv.Optional(CONF_WIPE_ON_BOOT, default=False): cv.All(
|
cv.Optional(CONF_WIPE_ON_BOOT, default=False): cv.All(
|
||||||
cv.boolean,
|
cv.Any(
|
||||||
|
cv.boolean,
|
||||||
|
cv.one_of(*["once"], lower=True),
|
||||||
|
),
|
||||||
cv.requires_component("nrf52"),
|
cv.requires_component("nrf52"),
|
||||||
),
|
),
|
||||||
|
cv.Optional(CONF_POWER_SOURCE, default="DC_SOURCE"): cv.enum(
|
||||||
|
POWER_SOURCE, upper=True
|
||||||
|
),
|
||||||
}
|
}
|
||||||
).extend(cv.COMPONENT_SCHEMA),
|
).extend(cv.COMPONENT_SCHEMA),
|
||||||
zigbee_set_core_data,
|
zigbee_set_core_data,
|
||||||
@@ -86,7 +95,16 @@ async def setup_binary_sensor(entity: cg.MockObj, config: ConfigType) -> None:
|
|||||||
await zephyr_setup_binary_sensor(entity, config)
|
await zephyr_setup_binary_sensor(entity, config)
|
||||||
|
|
||||||
|
|
||||||
def validate_binary_sensor(config: ConfigType) -> ConfigType:
|
async def setup_sensor(entity: cg.MockObj, config: ConfigType) -> None:
|
||||||
|
if not config.get(CONF_ZIGBEE_ID) or config.get(CONF_INTERNAL):
|
||||||
|
return
|
||||||
|
if CORE.using_zephyr:
|
||||||
|
from .zigbee_zephyr import zephyr_setup_sensor
|
||||||
|
|
||||||
|
await zephyr_setup_sensor(entity, config)
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
data: dict[str, Any] = CORE.data.setdefault(KEY_ZIGBEE, {})
|
data: dict[str, Any] = CORE.data.setdefault(KEY_ZIGBEE, {})
|
||||||
@@ -95,6 +113,14 @@ def validate_binary_sensor(config: ConfigType) -> ConfigType:
|
|||||||
return config
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
def validate_binary_sensor(config: ConfigType) -> ConfigType:
|
||||||
|
return consume_endpoint(config)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_sensor(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(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,12 +3,24 @@ import esphome.codegen as cg
|
|||||||
zigbee_ns = cg.esphome_ns.namespace("zigbee")
|
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")
|
||||||
|
|
||||||
CONF_MAX_EP_NUMBER = 8
|
CONF_MAX_EP_NUMBER = 8
|
||||||
CONF_ZIGBEE_ID = "zigbee_id"
|
CONF_ZIGBEE_ID = "zigbee_id"
|
||||||
CONF_ON_JOIN = "on_join"
|
CONF_ON_JOIN = "on_join"
|
||||||
CONF_WIPE_ON_BOOT = "wipe_on_boot"
|
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_POWER_SOURCE = "power_source"
|
||||||
|
POWER_SOURCE = {
|
||||||
|
"UNKNOWN": "ZB_ZCL_BASIC_POWER_SOURCE_UNKNOWN",
|
||||||
|
"MAINS_SINGLE_PHASE": "ZB_ZCL_BASIC_POWER_SOURCE_MAINS_SINGLE_PHASE",
|
||||||
|
"MAINS_THREE_PHASE": "ZB_ZCL_BASIC_POWER_SOURCE_MAINS_THREE_PHASE",
|
||||||
|
"BATTERY": "ZB_ZCL_BASIC_POWER_SOURCE_BATTERY",
|
||||||
|
"DC_SOURCE": "ZB_ZCL_BASIC_POWER_SOURCE_DC_SOURCE",
|
||||||
|
"EMERGENCY_MAINS_CONST": "ZB_ZCL_BASIC_POWER_SOURCE_EMERGENCY_MAINS_CONST",
|
||||||
|
"EMERGENCY_MAINS_TRANSF": "ZB_ZCL_BASIC_POWER_SOURCE_EMERGENCY_MAINS_TRANSF",
|
||||||
|
}
|
||||||
|
|
||||||
# Keys for CORE.data storage
|
# Keys for CORE.data storage
|
||||||
KEY_ZIGBEE = "zigbee"
|
KEY_ZIGBEE = "zigbee"
|
||||||
@@ -22,3 +34,4 @@ ZB_ZCL_IDENTIFY_ATTRS_T = "zb_zcl_identify_attrs_t"
|
|||||||
ZB_ZCL_CLUSTER_ID_BASIC = "ZB_ZCL_CLUSTER_ID_BASIC"
|
ZB_ZCL_CLUSTER_ID_BASIC = "ZB_ZCL_CLUSTER_ID_BASIC"
|
||||||
ZB_ZCL_CLUSTER_ID_IDENTIFY = "ZB_ZCL_CLUSTER_ID_IDENTIFY"
|
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"
|
||||||
|
|||||||
@@ -17,9 +17,9 @@ ZigbeeBinarySensor::ZigbeeBinarySensor(binary_sensor::BinarySensor *binary_senso
|
|||||||
void ZigbeeBinarySensor::setup() {
|
void ZigbeeBinarySensor::setup() {
|
||||||
this->binary_sensor_->add_on_state_callback([this](bool state) {
|
this->binary_sensor_->add_on_state_callback([this](bool state) {
|
||||||
this->cluster_attributes_->present_value = state ? ZB_TRUE : ZB_FALSE;
|
this->cluster_attributes_->present_value = state ? ZB_TRUE : ZB_FALSE;
|
||||||
ESP_LOGD(TAG, "Set attribute end point: %d, present_value %d", this->end_point_,
|
ESP_LOGD(TAG, "Set attribute endpoint: %d, present_value %d", this->endpoint_,
|
||||||
this->cluster_attributes_->present_value);
|
this->cluster_attributes_->present_value);
|
||||||
ZB_ZCL_SET_ATTRIBUTE(this->end_point_, ZB_ZCL_CLUSTER_ID_BINARY_INPUT, ZB_ZCL_CLUSTER_SERVER_ROLE,
|
ZB_ZCL_SET_ATTRIBUTE(this->endpoint_, ZB_ZCL_CLUSTER_ID_BINARY_INPUT, ZB_ZCL_CLUSTER_SERVER_ROLE,
|
||||||
ZB_ZCL_ATTR_BINARY_INPUT_PRESENT_VALUE_ID, &this->cluster_attributes_->present_value,
|
ZB_ZCL_ATTR_BINARY_INPUT_PRESENT_VALUE_ID, &this->cluster_attributes_->present_value,
|
||||||
ZB_FALSE);
|
ZB_FALSE);
|
||||||
this->parent_->flush();
|
this->parent_->flush();
|
||||||
@@ -29,8 +29,8 @@ void ZigbeeBinarySensor::setup() {
|
|||||||
void ZigbeeBinarySensor::dump_config() {
|
void ZigbeeBinarySensor::dump_config() {
|
||||||
ESP_LOGCONFIG(TAG,
|
ESP_LOGCONFIG(TAG,
|
||||||
"Zigbee Binary Sensor\n"
|
"Zigbee Binary Sensor\n"
|
||||||
" End point: %d, present_value %u",
|
" Endpoint: %d, present_value %u",
|
||||||
this->end_point_, this->cluster_attributes_->present_value);
|
this->endpoint_, this->cluster_attributes_->present_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace esphome::zigbee
|
} // namespace esphome::zigbee
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
#include "zigbee_sensor_zephyr.h"
|
||||||
|
#if defined(USE_ZIGBEE) && defined(USE_NRF52) && defined(USE_SENSOR)
|
||||||
|
#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.sensor";
|
||||||
|
|
||||||
|
ZigbeeSensor::ZigbeeSensor(sensor::Sensor *sensor) : sensor_(sensor) {}
|
||||||
|
|
||||||
|
void ZigbeeSensor::setup() {
|
||||||
|
this->sensor_->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_, state);
|
||||||
|
ZB_ZCL_SET_ATTRIBUTE(this->endpoint_, ZB_ZCL_CLUSTER_ID_ANALOG_INPUT, ZB_ZCL_CLUSTER_SERVER_ROLE,
|
||||||
|
ZB_ZCL_ATTR_ANALOG_INPUT_PRESENT_VALUE_ID,
|
||||||
|
(zb_uint8_t *) &this->cluster_attributes_->present_value, ZB_FALSE);
|
||||||
|
this->parent_->flush();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ZigbeeSensor::dump_config() {
|
||||||
|
ESP_LOGCONFIG(TAG,
|
||||||
|
"Zigbee Sensor\n"
|
||||||
|
" Endpoint: %d, present_value %f",
|
||||||
|
this->endpoint_, this->cluster_attributes_->present_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const zb_uint8_t ZB_ZCL_ANALOG_INPUT_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_INPUT_OUT_OF_SERVICE_ID:
|
||||||
|
ret = ZB_ZCL_CHECK_BOOL_VALUE(*value) ? RET_OK : RET_ERROR;
|
||||||
|
break;
|
||||||
|
case ZB_ZCL_ATTR_ANALOG_INPUT_PRESENT_VALUE_ID:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ZB_ZCL_ATTR_ANALOG_INPUT_STATUS_FLAG_ID:
|
||||||
|
if (*value > ZB_ZCL_ANALOG_INPUT_STATUS_FLAG_MAX_VALUE) {
|
||||||
|
ret = RET_ERROR;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace esphome::zigbee
|
||||||
|
|
||||||
|
void zb_zcl_analog_input_init_server() {
|
||||||
|
zb_zcl_add_cluster_handlers(ZB_ZCL_CLUSTER_ID_ANALOG_INPUT, 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_input_init_client() {
|
||||||
|
zb_zcl_add_cluster_handlers(ZB_ZCL_CLUSTER_ID_ANALOG_INPUT, 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
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esphome/components/zigbee/zigbee_zephyr.h"
|
||||||
|
#if defined(USE_ZIGBEE) && defined(USE_NRF52) && defined(USE_SENSOR)
|
||||||
|
#include "esphome/core/component.h"
|
||||||
|
#include "esphome/components/sensor/sensor.h"
|
||||||
|
extern "C" {
|
||||||
|
#include <zboss_api.h>
|
||||||
|
#include <zboss_api_addons.h>
|
||||||
|
}
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ZB_ZCL_ATTR_ANALOG_INPUT_DESCRIPTION_ID = 0x001C,
|
||||||
|
ZB_ZCL_ATTR_ANALOG_INPUT_OUT_OF_SERVICE_ID = 0x0051,
|
||||||
|
ZB_ZCL_ATTR_ANALOG_INPUT_PRESENT_VALUE_ID = 0x0055,
|
||||||
|
ZB_ZCL_ATTR_ANALOG_INPUT_STATUS_FLAG_ID = 0x006F,
|
||||||
|
ZB_ZCL_ATTR_ANALOG_INPUT_ENGINEERING_UNITS_ID = 0x0075,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define ZB_ZCL_ANALOG_INPUT_CLUSTER_REVISION_DEFAULT ((zb_uint16_t) 0x0001u)
|
||||||
|
|
||||||
|
#define ZB_SET_ATTR_DESCR_WITH_ZB_ZCL_ATTR_ANALOG_INPUT_DESCRIPTION_ID(data_ptr) \
|
||||||
|
{ \
|
||||||
|
ZB_ZCL_ATTR_ANALOG_INPUT_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_INPUT_OUT_OF_SERVICE_ID(data_ptr) \
|
||||||
|
{ \
|
||||||
|
ZB_ZCL_ATTR_ANALOG_INPUT_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) \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ZB_SET_ATTR_DESCR_WITH_ZB_ZCL_ATTR_ANALOG_INPUT_PRESENT_VALUE_ID(data_ptr) \
|
||||||
|
{ \
|
||||||
|
ZB_ZCL_ATTR_ANALOG_INPUT_PRESENT_VALUE_ID, ZB_ZCL_ATTR_TYPE_SINGLE, \
|
||||||
|
ZB_ZCL_ATTR_ACCESS_READ_ONLY | ZB_ZCL_ATTR_ACCESS_WRITE_OPTIONAL | ZB_ZCL_ATTR_ACCESS_REPORTING, \
|
||||||
|
(ZB_ZCL_NON_MANUFACTURER_SPECIFIC), (void *) (data_ptr) \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ZB_SET_ATTR_DESCR_WITH_ZB_ZCL_ATTR_ANALOG_INPUT_STATUS_FLAG_ID(data_ptr) \
|
||||||
|
{ \
|
||||||
|
ZB_ZCL_ATTR_ANALOG_INPUT_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_INPUT_ENGINEERING_UNITS_ID(data_ptr) \
|
||||||
|
{ \
|
||||||
|
ZB_ZCL_ATTR_ANALOG_INPUT_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_INPUT_ATTRIB_LIST(attr_list, out_of_service, present_value, status_flag, \
|
||||||
|
engineering_units, description) \
|
||||||
|
ZB_ZCL_START_DECLARE_ATTRIB_LIST_CLUSTER_REVISION(attr_list, ZB_ZCL_ANALOG_INPUT) \
|
||||||
|
ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_ANALOG_INPUT_OUT_OF_SERVICE_ID, (out_of_service)) \
|
||||||
|
ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_ANALOG_INPUT_PRESENT_VALUE_ID, (present_value)) \
|
||||||
|
ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_ANALOG_INPUT_STATUS_FLAG_ID, (status_flag)) \
|
||||||
|
ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_ANALOG_INPUT_ENGINEERING_UNITS_ID, (engineering_units)) \
|
||||||
|
ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_ANALOG_INPUT_DESCRIPTION_ID, (description)) \
|
||||||
|
ZB_ZCL_FINISH_DECLARE_ATTRIB_LIST
|
||||||
|
|
||||||
|
void zb_zcl_analog_input_init_server();
|
||||||
|
void zb_zcl_analog_input_init_client();
|
||||||
|
#define ZB_ZCL_CLUSTER_ID_ANALOG_INPUT_SERVER_ROLE_INIT zb_zcl_analog_input_init_server
|
||||||
|
#define ZB_ZCL_CLUSTER_ID_ANALOG_INPUT_CLIENT_ROLE_INIT zb_zcl_analog_input_init_client
|
||||||
|
|
||||||
|
namespace esphome::zigbee {
|
||||||
|
|
||||||
|
class ZigbeeSensor : public ZigbeeEntity, public Component {
|
||||||
|
public:
|
||||||
|
explicit ZigbeeSensor(sensor::Sensor *sensor);
|
||||||
|
void set_cluster_attributes(AnalogAttrs &cluster_attributes) { this->cluster_attributes_ = &cluster_attributes; }
|
||||||
|
|
||||||
|
void setup() override;
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
AnalogAttrs *cluster_attributes_{nullptr};
|
||||||
|
sensor::Sensor *sensor_{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace esphome::zigbee
|
||||||
|
#endif
|
||||||
@@ -138,9 +138,26 @@ void ZigbeeComponent::setup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_ZIGBEE_WIPE_ON_BOOT
|
#ifdef USE_ZIGBEE_WIPE_ON_BOOT
|
||||||
erase_flash_(FIXED_PARTITION_ID(ZBOSS_NVRAM));
|
bool wipe = true;
|
||||||
erase_flash_(FIXED_PARTITION_ID(ZBOSS_PRODUCT_CONFIG));
|
#ifdef USE_ZIGBEE_WIPE_ON_BOOT_MAGIC
|
||||||
erase_flash_(FIXED_PARTITION_ID(SETTINGS_STORAGE));
|
// unique hash to store preferences for this component
|
||||||
|
uint32_t hash = 88498616UL;
|
||||||
|
uint32_t wipe_value = 0;
|
||||||
|
auto wipe_pref = global_preferences->make_preference<uint32_t>(hash, true);
|
||||||
|
if (wipe_pref.load(&wipe_value)) {
|
||||||
|
wipe = wipe_value != USE_ZIGBEE_WIPE_ON_BOOT_MAGIC;
|
||||||
|
ESP_LOGD(TAG, "Wipe value in preferences %u, in firmware %u", wipe_value, USE_ZIGBEE_WIPE_ON_BOOT_MAGIC);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (wipe) {
|
||||||
|
erase_flash_(FIXED_PARTITION_ID(ZBOSS_NVRAM));
|
||||||
|
erase_flash_(FIXED_PARTITION_ID(ZBOSS_PRODUCT_CONFIG));
|
||||||
|
erase_flash_(FIXED_PARTITION_ID(SETTINGS_STORAGE));
|
||||||
|
#ifdef USE_ZIGBEE_WIPE_ON_BOOT_MAGIC
|
||||||
|
wipe_value = USE_ZIGBEE_WIPE_ON_BOOT_MAGIC;
|
||||||
|
wipe_pref.save(&wipe_value);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ZB_ZCL_REGISTER_DEVICE_CB(zcl_device_cb);
|
ZB_ZCL_REGISTER_DEVICE_CB(zcl_device_cb);
|
||||||
@@ -152,15 +169,54 @@ void ZigbeeComponent::setup() {
|
|||||||
zigbee_enable();
|
zigbee_enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZigbeeComponent::dump_config() {
|
static const char *role() {
|
||||||
bool wipe = false;
|
switch (zb_get_network_role()) {
|
||||||
|
case ZB_NWK_DEVICE_TYPE_COORDINATOR:
|
||||||
|
return "coordinator";
|
||||||
|
case ZB_NWK_DEVICE_TYPE_ROUTER:
|
||||||
|
return "router";
|
||||||
|
case ZB_NWK_DEVICE_TYPE_ED:
|
||||||
|
return "end device";
|
||||||
|
}
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *get_wipe_on_boot() {
|
||||||
#ifdef USE_ZIGBEE_WIPE_ON_BOOT
|
#ifdef USE_ZIGBEE_WIPE_ON_BOOT
|
||||||
wipe = true;
|
#ifdef USE_ZIGBEE_WIPE_ON_BOOT_MAGIC
|
||||||
|
return "ONCE";
|
||||||
|
#else
|
||||||
|
return "YES";
|
||||||
#endif
|
#endif
|
||||||
|
#else
|
||||||
|
return "NO";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ZigbeeComponent::dump_config() {
|
||||||
|
char ieee_addr_buf[IEEE_ADDR_BUF_SIZE] = {0};
|
||||||
|
zb_ieee_addr_t addr;
|
||||||
|
zb_get_long_address(addr);
|
||||||
|
ieee_addr_to_str(ieee_addr_buf, sizeof(ieee_addr_buf), addr);
|
||||||
|
zb_ext_pan_id_t extended_pan_id;
|
||||||
|
char extended_pan_id_buf[IEEE_ADDR_BUF_SIZE] = {0};
|
||||||
|
zb_get_extended_pan_id(extended_pan_id);
|
||||||
|
ieee_addr_to_str(extended_pan_id_buf, sizeof(extended_pan_id_buf), extended_pan_id);
|
||||||
ESP_LOGCONFIG(TAG,
|
ESP_LOGCONFIG(TAG,
|
||||||
"Zigbee\n"
|
"Zigbee\n"
|
||||||
" Wipe on boot: %s",
|
" Wipe on boot: %s\n"
|
||||||
YESNO(wipe));
|
" Device is joined to the network: %s\n"
|
||||||
|
" Current channel: %d\n"
|
||||||
|
" Current page: %d\n"
|
||||||
|
" Sleep threshold: %ums\n"
|
||||||
|
" Role: %s\n"
|
||||||
|
" Long addr: 0x%s\n"
|
||||||
|
" Short addr: 0x%04X\n"
|
||||||
|
" Long pan id: 0x%s\n"
|
||||||
|
" Short pan id: 0x%04X",
|
||||||
|
get_wipe_on_boot(), YESNO(zb_zdo_joined()), zb_get_current_channel(), zb_get_current_page(),
|
||||||
|
zb_get_sleep_threshold(), role(), ieee_addr_buf, zb_get_short_address(), extended_pan_id_buf,
|
||||||
|
zb_get_pan_id());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void send_attribute_report(zb_bufid_t bufid, zb_uint16_t cmd_id) {
|
static void send_attribute_report(zb_bufid_t bufid, zb_uint16_t cmd_id) {
|
||||||
|
|||||||
@@ -28,16 +28,15 @@ extern "C" {
|
|||||||
ESPHOME_CAT7(zb_af_simple_desc_, ep_name, _, in_num, _, out_num, _t)
|
ESPHOME_CAT7(zb_af_simple_desc_, ep_name, _, in_num, _, out_num, _t)
|
||||||
|
|
||||||
// needed to use ESPHOME_ZB_DECLARE_SIMPLE_DESC
|
// needed to use ESPHOME_ZB_DECLARE_SIMPLE_DESC
|
||||||
#define ESPHOME_ZB_ZCL_DECLARE_SIMPLE_DESC(ep_name, ep_id, in_clust_num, out_clust_num, ...) \
|
#define ESPHOME_ZB_ZCL_DECLARE_SIMPLE_DESC(ep_name, ep_id, in_clust_num, out_clust_num, app_device_id, ...) \
|
||||||
ESPHOME_ZB_DECLARE_SIMPLE_DESC(ep_name, in_clust_num, out_clust_num); \
|
ESPHOME_ZB_DECLARE_SIMPLE_DESC(ep_name, in_clust_num, out_clust_num); \
|
||||||
ESPHOME_ZB_AF_SIMPLE_DESC_TYPE(ep_name, in_clust_num, out_clust_num) \
|
ESPHOME_ZB_AF_SIMPLE_DESC_TYPE(ep_name, in_clust_num, out_clust_num) \
|
||||||
simple_desc_##ep_name = {ep_id, ZB_AF_HA_PROFILE_ID, ZB_HA_SIMPLE_SENSOR_DEVICE_ID, 0, 0, in_clust_num, \
|
simple_desc_##ep_name = {ep_id, ZB_AF_HA_PROFILE_ID, app_device_id, 0, 0, in_clust_num, out_clust_num, {__VA_ARGS__}}
|
||||||
out_clust_num, {__VA_ARGS__}}
|
|
||||||
|
|
||||||
// needed to use ESPHOME_ZB_ZCL_DECLARE_SIMPLE_DESC
|
// needed to use ESPHOME_ZB_ZCL_DECLARE_SIMPLE_DESC
|
||||||
#define ESPHOME_ZB_HA_DECLARE_EP(ep_name, ep_id, cluster_list, in_cluster_num, out_cluster_num, report_attr_count, \
|
#define ESPHOME_ZB_HA_DECLARE_EP(ep_name, ep_id, cluster_list, in_cluster_num, out_cluster_num, report_attr_count, \
|
||||||
...) \
|
app_device_id, ...) \
|
||||||
ESPHOME_ZB_ZCL_DECLARE_SIMPLE_DESC(ep_name, ep_id, in_cluster_num, out_cluster_num, __VA_ARGS__); \
|
ESPHOME_ZB_ZCL_DECLARE_SIMPLE_DESC(ep_name, ep_id, in_cluster_num, out_cluster_num, app_device_id, __VA_ARGS__); \
|
||||||
ZBOSS_DEVICE_DECLARE_REPORTING_CTX(reporting_info##ep_name, report_attr_count); \
|
ZBOSS_DEVICE_DECLARE_REPORTING_CTX(reporting_info##ep_name, report_attr_count); \
|
||||||
ZB_AF_DECLARE_ENDPOINT_DESC(ep_name, ep_id, ZB_AF_HA_PROFILE_ID, 0, NULL, \
|
ZB_AF_DECLARE_ENDPOINT_DESC(ep_name, ep_id, ZB_AF_HA_PROFILE_ID, 0, NULL, \
|
||||||
ZB_ZCL_ARRAY_SIZE(cluster_list, zb_zcl_cluster_desc_t), cluster_list, \
|
ZB_ZCL_ARRAY_SIZE(cluster_list, zb_zcl_cluster_desc_t), cluster_list, \
|
||||||
@@ -57,10 +56,8 @@ struct AnalogAttrs {
|
|||||||
zb_bool_t out_of_service;
|
zb_bool_t out_of_service;
|
||||||
float present_value;
|
float present_value;
|
||||||
zb_uint8_t status_flags;
|
zb_uint8_t status_flags;
|
||||||
|
zb_uint16_t engineering_units;
|
||||||
zb_uchar_t description[ZB_ZCL_MAX_STRING_SIZE];
|
zb_uchar_t description[ZB_ZCL_MAX_STRING_SIZE];
|
||||||
float max_present_value;
|
|
||||||
float min_present_value;
|
|
||||||
float resolution;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ZigbeeComponent : public Component {
|
class ZigbeeComponent : public Component {
|
||||||
@@ -93,10 +90,10 @@ class ZigbeeComponent : public Component {
|
|||||||
class ZigbeeEntity {
|
class ZigbeeEntity {
|
||||||
public:
|
public:
|
||||||
void set_parent(ZigbeeComponent *parent) { this->parent_ = parent; }
|
void set_parent(ZigbeeComponent *parent) { this->parent_ = parent; }
|
||||||
void set_end_point(zb_uint8_t end_point) { this->end_point_ = end_point; }
|
void set_endpoint(zb_uint8_t endpoint) { this->endpoint_ = endpoint; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
zb_uint8_t end_point_{0};
|
zb_uint8_t endpoint_{0};
|
||||||
ZigbeeComponent *parent_{nullptr};
|
ZigbeeComponent *parent_{nullptr};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,45 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import random
|
||||||
|
|
||||||
from esphome import automation
|
from esphome import automation
|
||||||
import esphome.codegen as cg
|
import esphome.codegen as cg
|
||||||
from esphome.components.zephyr import zephyr_add_prj_conf
|
from esphome.components.zephyr import zephyr_add_prj_conf
|
||||||
import esphome.config_validation as cv
|
import esphome.config_validation as cv
|
||||||
from esphome.const import CONF_ID, CONF_NAME, __version__
|
from esphome.const import (
|
||||||
|
CONF_ID,
|
||||||
|
CONF_NAME,
|
||||||
|
CONF_UNIT_OF_MEASUREMENT,
|
||||||
|
UNIT_AMPERE,
|
||||||
|
UNIT_CELSIUS,
|
||||||
|
UNIT_CENTIMETER,
|
||||||
|
UNIT_DECIBEL,
|
||||||
|
UNIT_HECTOPASCAL,
|
||||||
|
UNIT_HERTZ,
|
||||||
|
UNIT_HOUR,
|
||||||
|
UNIT_KELVIN,
|
||||||
|
UNIT_KILOMETER,
|
||||||
|
UNIT_KILOWATT,
|
||||||
|
UNIT_KILOWATT_HOURS,
|
||||||
|
UNIT_LUX,
|
||||||
|
UNIT_METER,
|
||||||
|
UNIT_MICROGRAMS_PER_CUBIC_METER,
|
||||||
|
UNIT_MILLIAMP,
|
||||||
|
UNIT_MILLIGRAMS_PER_CUBIC_METER,
|
||||||
|
UNIT_MILLIMETER,
|
||||||
|
UNIT_MILLISECOND,
|
||||||
|
UNIT_MILLIVOLT,
|
||||||
|
UNIT_MINUTE,
|
||||||
|
UNIT_OHM,
|
||||||
|
UNIT_PARTS_PER_BILLION,
|
||||||
|
UNIT_PARTS_PER_MILLION,
|
||||||
|
UNIT_PASCAL,
|
||||||
|
UNIT_PERCENT,
|
||||||
|
UNIT_SECOND,
|
||||||
|
UNIT_VOLT,
|
||||||
|
UNIT_WATT,
|
||||||
|
UNIT_WATT_HOURS,
|
||||||
|
__version__,
|
||||||
|
)
|
||||||
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
from esphome.core import CORE, CoroPriority, coroutine_with_priority
|
||||||
from esphome.cpp_generator import (
|
from esphome.cpp_generator import (
|
||||||
AssignmentExpression,
|
AssignmentExpression,
|
||||||
@@ -15,22 +50,63 @@ from esphome.types import ConfigType
|
|||||||
|
|
||||||
from .const_zephyr import (
|
from .const_zephyr import (
|
||||||
CONF_ON_JOIN,
|
CONF_ON_JOIN,
|
||||||
|
CONF_POWER_SOURCE,
|
||||||
CONF_WIPE_ON_BOOT,
|
CONF_WIPE_ON_BOOT,
|
||||||
CONF_ZIGBEE_BINARY_SENSOR,
|
CONF_ZIGBEE_BINARY_SENSOR,
|
||||||
CONF_ZIGBEE_ID,
|
CONF_ZIGBEE_ID,
|
||||||
|
CONF_ZIGBEE_SENSOR,
|
||||||
KEY_EP_NUMBER,
|
KEY_EP_NUMBER,
|
||||||
KEY_ZIGBEE,
|
KEY_ZIGBEE,
|
||||||
|
POWER_SOURCE,
|
||||||
ZB_ZCL_BASIC_ATTRS_EXT_T,
|
ZB_ZCL_BASIC_ATTRS_EXT_T,
|
||||||
|
ZB_ZCL_CLUSTER_ID_ANALOG_INPUT,
|
||||||
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_IDENTIFY,
|
ZB_ZCL_CLUSTER_ID_IDENTIFY,
|
||||||
ZB_ZCL_IDENTIFY_ATTRS_T,
|
ZB_ZCL_IDENTIFY_ATTRS_T,
|
||||||
|
AnalogAttrs,
|
||||||
BinaryAttrs,
|
BinaryAttrs,
|
||||||
ZigbeeComponent,
|
ZigbeeComponent,
|
||||||
zigbee_ns,
|
zigbee_ns,
|
||||||
)
|
)
|
||||||
|
|
||||||
ZigbeeBinarySensor = zigbee_ns.class_("ZigbeeBinarySensor", cg.Component)
|
ZigbeeBinarySensor = zigbee_ns.class_("ZigbeeBinarySensor", cg.Component)
|
||||||
|
ZigbeeSensor = zigbee_ns.class_("ZigbeeSensor", cg.Component)
|
||||||
|
|
||||||
|
# BACnet engineering units mapping (ZCL uses BACnet unit codes)
|
||||||
|
# See: https://github.com/zigpy/zha/blob/dev/zha/application/platforms/number/bacnet.py
|
||||||
|
BACNET_UNITS = {
|
||||||
|
UNIT_CELSIUS: 62,
|
||||||
|
UNIT_KELVIN: 63,
|
||||||
|
UNIT_VOLT: 5,
|
||||||
|
UNIT_MILLIVOLT: 124,
|
||||||
|
UNIT_AMPERE: 3,
|
||||||
|
UNIT_MILLIAMP: 2,
|
||||||
|
UNIT_OHM: 4,
|
||||||
|
UNIT_WATT: 47,
|
||||||
|
UNIT_KILOWATT: 48,
|
||||||
|
UNIT_WATT_HOURS: 18,
|
||||||
|
UNIT_KILOWATT_HOURS: 19,
|
||||||
|
UNIT_PASCAL: 53,
|
||||||
|
UNIT_HECTOPASCAL: 133,
|
||||||
|
UNIT_HERTZ: 27,
|
||||||
|
UNIT_MILLIMETER: 30,
|
||||||
|
UNIT_CENTIMETER: 118,
|
||||||
|
UNIT_METER: 31,
|
||||||
|
UNIT_KILOMETER: 193,
|
||||||
|
UNIT_MILLISECOND: 159,
|
||||||
|
UNIT_SECOND: 73,
|
||||||
|
UNIT_MINUTE: 72,
|
||||||
|
UNIT_HOUR: 71,
|
||||||
|
UNIT_PARTS_PER_MILLION: 96,
|
||||||
|
UNIT_PARTS_PER_BILLION: 97,
|
||||||
|
UNIT_MICROGRAMS_PER_CUBIC_METER: 219,
|
||||||
|
UNIT_MILLIGRAMS_PER_CUBIC_METER: 218,
|
||||||
|
UNIT_LUX: 37,
|
||||||
|
UNIT_DECIBEL: 199,
|
||||||
|
UNIT_PERCENT: 98,
|
||||||
|
}
|
||||||
|
BACNET_UNIT_NO_UNITS = 95
|
||||||
|
|
||||||
zephyr_binary_sensor = cv.Schema(
|
zephyr_binary_sensor = cv.Schema(
|
||||||
{
|
{
|
||||||
@@ -41,6 +117,15 @@ zephyr_binary_sensor = cv.Schema(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
zephyr_sensor = cv.Schema(
|
||||||
|
{
|
||||||
|
cv.OnlyWith(CONF_ZIGBEE_ID, ["nrf52", "zigbee"]): cv.use_id(ZigbeeComponent),
|
||||||
|
cv.OnlyWith(CONF_ZIGBEE_SENSOR, ["nrf52", "zigbee"]): cv.declare_id(
|
||||||
|
ZigbeeSensor
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
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)
|
||||||
@@ -56,6 +141,10 @@ async def zephyr_to_code(config: ConfigType) -> None:
|
|||||||
zephyr_add_prj_conf("NET_UDP", False)
|
zephyr_add_prj_conf("NET_UDP", False)
|
||||||
|
|
||||||
if config[CONF_WIPE_ON_BOOT]:
|
if config[CONF_WIPE_ON_BOOT]:
|
||||||
|
if config[CONF_WIPE_ON_BOOT] == "once":
|
||||||
|
cg.add_define(
|
||||||
|
"USE_ZIGBEE_WIPE_ON_BOOT_MAGIC", random.randint(0x000001, 0xFFFFFF)
|
||||||
|
)
|
||||||
cg.add_define("USE_ZIGBEE_WIPE_ON_BOOT")
|
cg.add_define("USE_ZIGBEE_WIPE_ON_BOOT")
|
||||||
var = cg.new_Pvariable(config[CONF_ID])
|
var = cg.new_Pvariable(config[CONF_ID])
|
||||||
|
|
||||||
@@ -85,7 +174,7 @@ async def _attr_to_code(config: ConfigType) -> None:
|
|||||||
),
|
),
|
||||||
zigbee_assign(
|
zigbee_assign(
|
||||||
basic_attrs.power_source,
|
basic_attrs.power_source,
|
||||||
cg.RawExpression("ZB_ZCL_BASIC_POWER_SOURCE_DC_SOURCE"),
|
cg.RawExpression(POWER_SOURCE[config[CONF_POWER_SOURCE]]),
|
||||||
),
|
),
|
||||||
zigbee_set_string(basic_attrs.location_id, ""),
|
zigbee_set_string(basic_attrs.location_id, ""),
|
||||||
zigbee_assign(
|
zigbee_assign(
|
||||||
@@ -191,6 +280,7 @@ def zigbee_register_ep(
|
|||||||
report_attr_count: int,
|
report_attr_count: int,
|
||||||
clusters: list[ZigbeeClusterDesc],
|
clusters: list[ZigbeeClusterDesc],
|
||||||
slot_index: int,
|
slot_index: int,
|
||||||
|
app_device_id: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Register a Zigbee endpoint."""
|
"""Register a Zigbee endpoint."""
|
||||||
in_cluster_num = sum(1 for c in clusters if c.has_attrs)
|
in_cluster_num = sum(1 for c in clusters if c.has_attrs)
|
||||||
@@ -204,7 +294,7 @@ def zigbee_register_ep(
|
|||||||
ep_id = slot_index + 1 # Endpoints are 1-indexed
|
ep_id = slot_index + 1 # Endpoints are 1-indexed
|
||||||
obj = cg.RawExpression(
|
obj = cg.RawExpression(
|
||||||
f"ESPHOME_ZB_HA_DECLARE_EP({ep_name}, {ep_id}, {cluster_list_name}, "
|
f"ESPHOME_ZB_HA_DECLARE_EP({ep_name}, {ep_id}, {cluster_list_name}, "
|
||||||
f"{in_cluster_num}, {out_cluster_num}, {report_attr_count}, {', '.join(cluster_ids)})"
|
f"{in_cluster_num}, {out_cluster_num}, {report_attr_count}, {app_device_id}, {', '.join(cluster_ids)})"
|
||||||
)
|
)
|
||||||
CORE.add_global(obj)
|
CORE.add_global(obj)
|
||||||
|
|
||||||
@@ -224,42 +314,102 @@ async def zephyr_setup_binary_sensor(entity: cg.MockObj, config: ConfigType) ->
|
|||||||
CORE.add_job(_add_binary_sensor, entity, config)
|
CORE.add_job(_add_binary_sensor, entity, config)
|
||||||
|
|
||||||
|
|
||||||
async def _add_binary_sensor(entity: cg.MockObj, config: ConfigType) -> None:
|
async def zephyr_setup_sensor(entity: cg.MockObj, config: ConfigType) -> None:
|
||||||
# Find the next available endpoint slot
|
CORE.add_job(_add_sensor, entity, config)
|
||||||
slot_index = next(
|
|
||||||
|
|
||||||
|
def _slot_index() -> int:
|
||||||
|
"""Find the next available endpoint slot"""
|
||||||
|
slot = next(
|
||||||
(i for i, v in enumerate(CORE.data[KEY_ZIGBEE][KEY_EP_NUMBER]) if v == ""), None
|
(i for i, v in enumerate(CORE.data[KEY_ZIGBEE][KEY_EP_NUMBER]) if v == ""), None
|
||||||
)
|
)
|
||||||
|
if slot is None:
|
||||||
|
raise cv.Invalid(
|
||||||
|
f"Not found empty slot, size ({len(CORE.data[KEY_ZIGBEE][KEY_EP_NUMBER])})"
|
||||||
|
)
|
||||||
|
return slot
|
||||||
|
|
||||||
|
|
||||||
|
async def _add_zigbee_input(
|
||||||
|
entity: cg.MockObj,
|
||||||
|
config: ConfigType,
|
||||||
|
component_key,
|
||||||
|
attrs_type,
|
||||||
|
zcl_macro: str,
|
||||||
|
cluster_id: str,
|
||||||
|
app_device_id: str,
|
||||||
|
extra_field_values: dict[str, int] | None = None,
|
||||||
|
) -> None:
|
||||||
|
slot_index = _slot_index()
|
||||||
|
|
||||||
# Create unique names for this sensor's variables based on slot index
|
|
||||||
prefix = f"zigbee_ep{slot_index + 1}"
|
prefix = f"zigbee_ep{slot_index + 1}"
|
||||||
attrs_name = f"{prefix}_binary_attrs"
|
attrs_name = f"{prefix}_attrs"
|
||||||
attr_list_name = f"{prefix}_binary_input_attrib_list"
|
attr_list_name = f"{prefix}_attrib_list"
|
||||||
cluster_list_name = f"{prefix}_cluster_list"
|
cluster_list_name = f"{prefix}_cluster_list"
|
||||||
ep_name = f"{prefix}_ep"
|
ep_name = f"{prefix}_ep"
|
||||||
|
|
||||||
# Create the binary attributes structure
|
# Create attribute struct
|
||||||
binary_attrs = zigbee_new_variable(attrs_name, BinaryAttrs)
|
attrs = zigbee_new_variable(attrs_name, attrs_type)
|
||||||
attr_list = zigbee_new_attr_list(
|
|
||||||
attr_list_name,
|
# Build attribute list args
|
||||||
"ESPHOME_ZB_ZCL_DECLARE_BINARY_INPUT_ATTRIB_LIST",
|
attr_args = [
|
||||||
zigbee_assign(binary_attrs.out_of_service, 0),
|
zigbee_assign(attrs.out_of_service, 0),
|
||||||
zigbee_assign(binary_attrs.present_value, 0),
|
zigbee_assign(attrs.present_value, 0),
|
||||||
zigbee_assign(binary_attrs.status_flags, 0),
|
zigbee_assign(attrs.status_flags, 0),
|
||||||
zigbee_set_string(binary_attrs.description, config[CONF_NAME]),
|
]
|
||||||
)
|
# Add extra field assignments (e.g., engineering_units for sensors)
|
||||||
|
if extra_field_values:
|
||||||
|
for field_name, value in extra_field_values.items():
|
||||||
|
attr_args.append(zigbee_assign(getattr(attrs, field_name), value))
|
||||||
|
attr_args.append(zigbee_set_string(attrs.description, config[CONF_NAME]))
|
||||||
|
|
||||||
|
# Create attribute list
|
||||||
|
attr_list = zigbee_new_attr_list(attr_list_name, zcl_macro, *attr_args)
|
||||||
|
|
||||||
# Create cluster list and register endpoint
|
# Create cluster list and register endpoint
|
||||||
cluster_list_name, clusters = zigbee_new_cluster_list(
|
cluster_list_name, clusters = zigbee_new_cluster_list(
|
||||||
cluster_list_name,
|
cluster_list_name,
|
||||||
[ZigbeeClusterDesc(ZB_ZCL_CLUSTER_ID_BINARY_INPUT, attr_list)],
|
[ZigbeeClusterDesc(cluster_id, attr_list)],
|
||||||
|
)
|
||||||
|
zigbee_register_ep(
|
||||||
|
ep_name, cluster_list_name, 2, clusters, slot_index, app_device_id
|
||||||
)
|
)
|
||||||
zigbee_register_ep(ep_name, cluster_list_name, 2, clusters, slot_index)
|
|
||||||
|
|
||||||
# Create the ZigbeeBinarySensor component
|
# Create ESPHome component
|
||||||
var = cg.new_Pvariable(config[CONF_ZIGBEE_BINARY_SENSOR], entity)
|
var = cg.new_Pvariable(config[component_key], entity)
|
||||||
await cg.register_component(var, config)
|
await cg.register_component(var, {})
|
||||||
|
|
||||||
|
cg.add(var.set_endpoint(slot_index + 1))
|
||||||
|
cg.add(var.set_cluster_attributes(attrs))
|
||||||
|
|
||||||
cg.add(var.set_end_point(slot_index + 1))
|
|
||||||
cg.add(var.set_cluster_attributes(binary_attrs))
|
|
||||||
hub = await cg.get_variable(config[CONF_ZIGBEE_ID])
|
hub = await cg.get_variable(config[CONF_ZIGBEE_ID])
|
||||||
cg.add(var.set_parent(hub))
|
cg.add(var.set_parent(hub))
|
||||||
|
|
||||||
|
|
||||||
|
async def _add_binary_sensor(entity: cg.MockObj, config: ConfigType) -> None:
|
||||||
|
await _add_zigbee_input(
|
||||||
|
entity,
|
||||||
|
config,
|
||||||
|
CONF_ZIGBEE_BINARY_SENSOR,
|
||||||
|
BinaryAttrs,
|
||||||
|
"ESPHOME_ZB_ZCL_DECLARE_BINARY_INPUT_ATTRIB_LIST",
|
||||||
|
ZB_ZCL_CLUSTER_ID_BINARY_INPUT,
|
||||||
|
"ZB_HA_SIMPLE_SENSOR_DEVICE_ID",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def _add_sensor(entity: cg.MockObj, config: ConfigType) -> 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_input(
|
||||||
|
entity,
|
||||||
|
config,
|
||||||
|
CONF_ZIGBEE_SENSOR,
|
||||||
|
AnalogAttrs,
|
||||||
|
"ESPHOME_ZB_ZCL_DECLARE_ANALOG_INPUT_ATTRIB_LIST",
|
||||||
|
ZB_ZCL_CLUSTER_ID_ANALOG_INPUT,
|
||||||
|
"ZB_HA_CUSTOM_ATTR_DEVICE_ID",
|
||||||
|
extra_field_values={"engineering_units": bacnet_unit},
|
||||||
|
)
|
||||||
|
|||||||
@@ -305,6 +305,7 @@
|
|||||||
#define USE_SOFTDEVICE_VERSION 1
|
#define USE_SOFTDEVICE_VERSION 1
|
||||||
#define USE_ZIGBEE
|
#define USE_ZIGBEE
|
||||||
#define USE_ZIGBEE_WIPE_ON_BOOT
|
#define USE_ZIGBEE_WIPE_ON_BOOT
|
||||||
|
#define USE_ZIGBEE_WIPE_ON_BOOT_MAGIC 1
|
||||||
#define ZIGBEE_ENDPOINTS_COUNT 8
|
#define ZIGBEE_ENDPOINTS_COUNT 8
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -15,10 +15,14 @@ binary_sensor:
|
|||||||
- platform: template
|
- platform: template
|
||||||
name: "Garage Door Open 7"
|
name: "Garage Door Open 7"
|
||||||
internal: True
|
internal: True
|
||||||
|
|
||||||
|
sensor:
|
||||||
- platform: template
|
- platform: template
|
||||||
name: "Garage Door Open 8"
|
name: "Analog 1"
|
||||||
|
lambda: return 10.0;
|
||||||
- platform: template
|
- platform: template
|
||||||
name: "Garage Door Open 9"
|
name: "Analog 2"
|
||||||
|
lambda: return 11.0;
|
||||||
|
|
||||||
zigbee:
|
zigbee:
|
||||||
wipe_on_boot: true
|
wipe_on_boot: true
|
||||||
|
|||||||
@@ -1 +1,5 @@
|
|||||||
<<: !include common.yaml
|
<<: !include common.yaml
|
||||||
|
|
||||||
|
zigbee:
|
||||||
|
wipe_on_boot: once
|
||||||
|
power_source: battery
|
||||||
|
|||||||
Reference in New Issue
Block a user