mirror of
https://github.com/esphome/esphome.git
synced 2025-06-15 14:56:59 +02:00
Merge 77099599ea61939d06a7b16fbe687fdf0aa65c35 into 07cf6e723bdd93a738bfc033ad9a932c1bf74b09
This commit is contained in:
commit
9c56a807eb
309
esphome/components/zigbee/__init__.py
Normal file
309
esphome/components/zigbee/__init__.py
Normal file
@ -0,0 +1,309 @@
|
||||
from datetime import datetime
|
||||
|
||||
# import random
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.zephyr import zephyr_add_prj_conf
|
||||
from esphome.components.zigbee_ctx import (
|
||||
KEY_EP_NUMBER,
|
||||
KEY_ZIGBEE,
|
||||
zigbee_set_core_data,
|
||||
)
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, __version__
|
||||
from esphome.core import CORE, ID, coroutine_with_priority
|
||||
from esphome.cpp_generator import (
|
||||
AssignmentExpression,
|
||||
MockObj,
|
||||
VariableDeclarationExpression,
|
||||
)
|
||||
|
||||
from .const import (
|
||||
CONF_BASIC_ATTRIB_LIST_EXT,
|
||||
CONF_BASIC_ATTRS_EXT,
|
||||
CONF_CLUSTER_LIST,
|
||||
CONF_EP,
|
||||
CONF_GROUPS_ATTRIB_LIST,
|
||||
CONF_GROUPS_ATTRS,
|
||||
CONF_IDENTIFY_ATTRIB_LIST,
|
||||
CONF_IDENTIFY_ATTRS,
|
||||
CONF_MAX_EP_NUMBER,
|
||||
CONF_SCENES_ATTRIB_LIST,
|
||||
CONF_SCENES_ATTRS,
|
||||
CONF_ZIGBEE_ID,
|
||||
ESPHOME_ZB_HA_DECLARE_EP,
|
||||
ZB_ZCL_CLUSTER_ID_BASIC,
|
||||
ZB_ZCL_CLUSTER_ID_GROUPS,
|
||||
ZB_ZCL_CLUSTER_ID_IDENTIFY,
|
||||
ZB_ZCL_CLUSTER_ID_SCENES,
|
||||
ZB_ZCL_DECLARE_BASIC_ATTRIB_LIST_EXT,
|
||||
ZB_ZCL_DECLARE_GROUPS_ATTRIB_LIST,
|
||||
ZB_ZCL_DECLARE_IDENTIFY_ATTRIB_LIST,
|
||||
ZB_ZCL_DECLARE_SCENES_ATTRIB_LIST,
|
||||
Zigbee,
|
||||
zb_char_t_ptr,
|
||||
zb_zcl_basic_attrs_ext_t,
|
||||
zb_zcl_groups_attrs_t,
|
||||
zb_zcl_identify_attrs_t,
|
||||
zb_zcl_scenes_attrs_t,
|
||||
zigbee_ns,
|
||||
)
|
||||
|
||||
AUTO_LOAD = ["zigbee_ctx"]
|
||||
|
||||
CONF_ON_JOIN = "on_join"
|
||||
|
||||
ZigbeeBaseSchema = cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_ZIGBEE_ID): cv.use_id(Zigbee),
|
||||
cv.GenerateID(CONF_BASIC_ATTRIB_LIST_EXT): cv.use_id(
|
||||
ZB_ZCL_DECLARE_BASIC_ATTRIB_LIST_EXT
|
||||
),
|
||||
cv.GenerateID(CONF_IDENTIFY_ATTRIB_LIST): cv.use_id(
|
||||
ZB_ZCL_DECLARE_IDENTIFY_ATTRIB_LIST
|
||||
),
|
||||
cv.GenerateID(CONF_GROUPS_ATTRIB_LIST): cv.use_id(
|
||||
ZB_ZCL_DECLARE_GROUPS_ATTRIB_LIST
|
||||
),
|
||||
cv.GenerateID(CONF_SCENES_ATTRIB_LIST): cv.use_id(
|
||||
ZB_ZCL_DECLARE_SCENES_ATTRIB_LIST
|
||||
),
|
||||
cv.GenerateID(CONF_EP): cv.declare_id(ESPHOME_ZB_HA_DECLARE_EP),
|
||||
cv.GenerateID(CONF_CLUSTER_LIST): cv.declare_id(
|
||||
cg.global_ns.namespace("zb_zcl_cluster_desc_t")
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(CONF_ID): cv.declare_id(Zigbee),
|
||||
cv.GenerateID(CONF_BASIC_ATTRS_EXT): cv.declare_id(
|
||||
zb_zcl_basic_attrs_ext_t
|
||||
),
|
||||
cv.GenerateID(CONF_BASIC_ATTRIB_LIST_EXT): cv.declare_id(
|
||||
ZB_ZCL_DECLARE_BASIC_ATTRIB_LIST_EXT
|
||||
),
|
||||
cv.GenerateID(CONF_IDENTIFY_ATTRS): cv.declare_id(zb_zcl_identify_attrs_t),
|
||||
cv.GenerateID(CONF_IDENTIFY_ATTRIB_LIST): cv.declare_id(
|
||||
ZB_ZCL_DECLARE_IDENTIFY_ATTRIB_LIST
|
||||
),
|
||||
cv.GenerateID(CONF_GROUPS_ATTRS): cv.declare_id(zb_zcl_groups_attrs_t),
|
||||
cv.GenerateID(CONF_GROUPS_ATTRIB_LIST): cv.declare_id(
|
||||
ZB_ZCL_DECLARE_GROUPS_ATTRIB_LIST
|
||||
),
|
||||
cv.GenerateID(CONF_SCENES_ATTRS): cv.declare_id(zb_zcl_scenes_attrs_t),
|
||||
cv.GenerateID(CONF_SCENES_ATTRIB_LIST): cv.declare_id(
|
||||
ZB_ZCL_DECLARE_SCENES_ATTRIB_LIST
|
||||
),
|
||||
cv.Optional(CONF_ON_JOIN): automation.validate_automation(single=True),
|
||||
}
|
||||
).extend(cv.COMPONENT_SCHEMA),
|
||||
zigbee_set_core_data,
|
||||
)
|
||||
|
||||
|
||||
def validate_number_of_ep(config):
|
||||
count = len(CORE.data[KEY_ZIGBEE][KEY_EP_NUMBER])
|
||||
if count > CONF_MAX_EP_NUMBER:
|
||||
raise cv.Invalid(f"Maximum number of EP is {CONF_MAX_EP_NUMBER}")
|
||||
if count == 0:
|
||||
raise cv.Invalid("At least one zigbee device need to be included")
|
||||
|
||||
|
||||
FINAL_VALIDATE_SCHEMA = cv.All(
|
||||
validate_number_of_ep,
|
||||
)
|
||||
|
||||
|
||||
@coroutine_with_priority(100.0)
|
||||
async def to_code(config):
|
||||
cg.add_define("USE_ZIGBEE")
|
||||
# zigbee
|
||||
zephyr_add_prj_conf("ZIGBEE", True)
|
||||
zephyr_add_prj_conf("ZIGBEE_APP_UTILS", True)
|
||||
zephyr_add_prj_conf("ZIGBEE_ROLE_END_DEVICE", True)
|
||||
|
||||
zephyr_add_prj_conf("ZIGBEE_CHANNEL_SELECTION_MODE_MULTI", True)
|
||||
|
||||
# TODO zigbee2mqtt do not update configuration of device without this
|
||||
# zephyr_add_prj_conf("IEEE802154_VENDOR_OUI_ENABLE", True)
|
||||
# random_number = random.randint(0x000000, 0xFFFFFF)
|
||||
# zephyr_add_prj_conf("IEEE802154_VENDOR_OUI", random_number)
|
||||
|
||||
# crypto
|
||||
zephyr_add_prj_conf("CRYPTO", True)
|
||||
|
||||
# networking
|
||||
zephyr_add_prj_conf("NET_IPV6", False)
|
||||
zephyr_add_prj_conf("NET_IP_ADDR_CHECK", False)
|
||||
zephyr_add_prj_conf("NET_UDP", False)
|
||||
|
||||
basic_attrs_ext = zigbee_new_variable(config[CONF_BASIC_ATTRS_EXT])
|
||||
zigbee_new_attr_list(
|
||||
config[CONF_BASIC_ATTRIB_LIST_EXT],
|
||||
zigbee_assign(
|
||||
basic_attrs_ext.zcl_version, cg.global_ns.namespace("ZB_ZCL_VERSION")
|
||||
),
|
||||
zigbee_assign(basic_attrs_ext.app_version, 0),
|
||||
zigbee_assign(basic_attrs_ext.stack_version, 0),
|
||||
zigbee_assign(basic_attrs_ext.hw_version, 0),
|
||||
zigbee_set_string(basic_attrs_ext.mf_name, "esphome"),
|
||||
zigbee_set_string(basic_attrs_ext.model_id, "v1"),
|
||||
zigbee_set_string(
|
||||
basic_attrs_ext.date_code, datetime.now().strftime("%d/%m/%y %H:%M")
|
||||
),
|
||||
zigbee_assign(
|
||||
basic_attrs_ext.power_source,
|
||||
cg.global_ns.namespace("ZB_ZCL_BASIC_POWER_SOURCE_DC_SOURCE"),
|
||||
),
|
||||
zigbee_set_string(basic_attrs_ext.location_id, ""),
|
||||
zigbee_assign(
|
||||
basic_attrs_ext.ph_env,
|
||||
cg.global_ns.namespace("ZB_ZCL_BASIC_ENV_UNSPECIFIED"),
|
||||
),
|
||||
zigbee_set_string(basic_attrs_ext.sw_ver, __version__),
|
||||
)
|
||||
|
||||
identify_attrs = zigbee_new_variable(config[CONF_IDENTIFY_ATTRS])
|
||||
zigbee_new_attr_list(
|
||||
config[CONF_IDENTIFY_ATTRIB_LIST],
|
||||
zigbee_assign(
|
||||
identify_attrs.identify_time,
|
||||
cg.global_ns.namespace("ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE"),
|
||||
),
|
||||
)
|
||||
|
||||
groups_attrs = zigbee_new_variable(config[CONF_GROUPS_ATTRS])
|
||||
zigbee_new_attr_list(
|
||||
config[CONF_GROUPS_ATTRIB_LIST],
|
||||
zigbee_assign(groups_attrs.name_support, 0),
|
||||
)
|
||||
|
||||
scenes_attrs = zigbee_new_variable(config[CONF_SCENES_ATTRS])
|
||||
zigbee_new_attr_list(
|
||||
config[CONF_SCENES_ATTRIB_LIST],
|
||||
zigbee_assign(scenes_attrs.scene_count, 0),
|
||||
zigbee_assign(scenes_attrs.current_scene, 0),
|
||||
zigbee_assign(scenes_attrs.current_group, 0),
|
||||
zigbee_assign(scenes_attrs.scene_valid, 0),
|
||||
zigbee_assign(scenes_attrs.name_support, 0),
|
||||
)
|
||||
|
||||
# the rest
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
|
||||
if on_join_config := config.get(CONF_ON_JOIN):
|
||||
await automation.build_automation(var.get_join_trigger(), [], on_join_config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
|
||||
FactoryResetAction = zigbee_ns.class_("FactoryResetAction", automation.Action)
|
||||
|
||||
|
||||
@automation.register_action("zigbee.factory_reset", FactoryResetAction, cv.Schema({}))
|
||||
async def zigbee_factory_reset_to_code(config, action_id, template_arg, args):
|
||||
return cg.new_Pvariable(action_id, template_arg)
|
||||
|
||||
|
||||
def zigbee_new_variable(id_: ID, type_: "MockObj" = None) -> "MockObj":
|
||||
assert isinstance(id_, ID)
|
||||
obj = MockObj(id_, ".")
|
||||
if type_ is not None:
|
||||
id_.type = type_
|
||||
decl = VariableDeclarationExpression(id_.type, "", id_)
|
||||
CORE.add_global(decl)
|
||||
CORE.register_variable(id_, obj)
|
||||
return obj
|
||||
|
||||
|
||||
def zigbee_assign(target, expression):
|
||||
cg.add(AssignmentExpression("", "", target, expression))
|
||||
return target
|
||||
|
||||
|
||||
def zigbee_set_string(target, value: str):
|
||||
cg.add(
|
||||
cg.RawExpression(
|
||||
f"ZB_ZCL_SET_STRING_VAL({target}, {cg.safe_exp(value)}, ZB_ZCL_STRING_CONST_SIZE({cg.safe_exp(value)}))"
|
||||
)
|
||||
)
|
||||
return ID(str(target), True, zb_char_t_ptr)
|
||||
|
||||
|
||||
def zigbee_new_attr_list(id_: ID, *args):
|
||||
assert isinstance(id_, ID)
|
||||
list = []
|
||||
for arg in args:
|
||||
if str(zb_char_t_ptr) == str(arg.type) or (
|
||||
str(arg) == "zb_zcl_time_attrs_t_id"
|
||||
):
|
||||
list.append(f"{arg}")
|
||||
else:
|
||||
list.append(f"&{arg}")
|
||||
|
||||
obj = cg.RawExpression(f"{id_.type}({id_}, {', '.join(list)})")
|
||||
CORE.add_global(obj)
|
||||
CORE.register_variable(id_, obj)
|
||||
return id_
|
||||
|
||||
|
||||
class ArrayAssignmentExpression(AssignmentExpression):
|
||||
__slots__ = ()
|
||||
|
||||
def __init__(self, type_, name, rhs):
|
||||
super().__init__(type_, "", name, rhs)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.type} {self.name}[] = {self.rhs}"
|
||||
|
||||
|
||||
def zigbee_array(id_, rhs) -> "MockObj":
|
||||
rhs = cg.safe_exp(rhs)
|
||||
obj = MockObj(id_, ".")
|
||||
assignment = ArrayAssignmentExpression(id_.type, id_, rhs)
|
||||
CORE.add_global(assignment)
|
||||
CORE.register_variable(id_, obj)
|
||||
return obj
|
||||
|
||||
|
||||
class ZigbeeClusterDesc(MockObj):
|
||||
def __init__(self, name: str, attr=None):
|
||||
self.name = name
|
||||
self.attr = attr
|
||||
|
||||
def __str__(self):
|
||||
role = (
|
||||
"ZB_ZCL_CLUSTER_SERVER_ROLE" if self.attr else "ZB_ZCL_CLUSTER_CLIENT_ROLE"
|
||||
)
|
||||
attr_count = "0"
|
||||
attr_desc_list = "NULL"
|
||||
if self.attr:
|
||||
attr_count = f"ZB_ZCL_ARRAY_SIZE({self.attr}, zb_zcl_attr_t)"
|
||||
attr_desc_list = str(self.attr)
|
||||
return f"ZB_ZCL_CLUSTER_DESC({self.name}, {attr_count}, {attr_desc_list}, {role}, ZB_ZCL_MANUF_CODE_INVALID)"
|
||||
|
||||
|
||||
def zigbee_new_cluster_list(config, attr_list):
|
||||
rhs = [
|
||||
ZigbeeClusterDesc(ZB_ZCL_CLUSTER_ID_BASIC, config[CONF_BASIC_ATTRIB_LIST_EXT]),
|
||||
ZigbeeClusterDesc(
|
||||
ZB_ZCL_CLUSTER_ID_IDENTIFY, config[CONF_IDENTIFY_ATTRIB_LIST]
|
||||
),
|
||||
]
|
||||
rhs.extend([attr_list[0]])
|
||||
rhs.extend(
|
||||
[
|
||||
ZigbeeClusterDesc(
|
||||
ZB_ZCL_CLUSTER_ID_GROUPS, config[CONF_GROUPS_ATTRIB_LIST]
|
||||
),
|
||||
ZigbeeClusterDesc(
|
||||
ZB_ZCL_CLUSTER_ID_SCENES, config[CONF_SCENES_ATTRIB_LIST]
|
||||
),
|
||||
]
|
||||
)
|
||||
if len(attr_list) == 2:
|
||||
rhs.extend([attr_list[1]])
|
||||
obj = zigbee_array(config[CONF_CLUSTER_LIST], rhs)
|
||||
return (obj, rhs)
|
105
esphome/components/zigbee/binary_sensor/__init__.py
Normal file
105
esphome/components/zigbee/binary_sensor/__init__.py
Normal file
@ -0,0 +1,105 @@
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import binary_sensor
|
||||
from esphome.components.zigbee_ctx import (
|
||||
KEY_EP_NUMBER,
|
||||
consume_ep_slots,
|
||||
zigbee_register_ep,
|
||||
)
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_LAMBDA, CONF_NAME, CONF_STATE
|
||||
from esphome.core import coroutine_with_priority
|
||||
|
||||
from .. import (
|
||||
ZigbeeBaseSchema,
|
||||
ZigbeeClusterDesc,
|
||||
zigbee_assign,
|
||||
zigbee_new_attr_list,
|
||||
zigbee_new_cluster_list,
|
||||
zigbee_new_variable,
|
||||
zigbee_set_string,
|
||||
)
|
||||
from ..const import (
|
||||
CONF_BINARY_ATTRS,
|
||||
CONF_BINARY_INPUT_ATTRIB_LIST,
|
||||
CONF_ZIGBEE_ID,
|
||||
ZB_ZCL_CLUSTER_ID_BINARY_INPUT,
|
||||
BinaryAttrs,
|
||||
zigbee_ns,
|
||||
)
|
||||
|
||||
AUTO_LOAD = ["zigbee"]
|
||||
|
||||
ZigbeeBinarySensor = zigbee_ns.class_(
|
||||
"ZigbeeBinarySensor", binary_sensor.BinarySensor, cg.Component
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
(
|
||||
binary_sensor.binary_sensor_schema(ZigbeeBinarySensor)
|
||||
.extend(
|
||||
{
|
||||
cv.GenerateID(CONF_BINARY_ATTRS): cv.declare_id(BinaryAttrs),
|
||||
cv.GenerateID(CONF_BINARY_INPUT_ATTRIB_LIST): cv.declare_id(
|
||||
cg.global_ns.namespace(
|
||||
"ESPHOME_ZB_ZCL_DECLARE_BINARY_INPUT_ATTRIB_LIST"
|
||||
)
|
||||
),
|
||||
cv.Optional(CONF_LAMBDA): cv.returning_lambda,
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
.extend(ZigbeeBaseSchema)
|
||||
),
|
||||
consume_ep_slots,
|
||||
)
|
||||
|
||||
|
||||
@coroutine_with_priority(50.0)
|
||||
async def to_code(config):
|
||||
binary_attrs = zigbee_new_variable(config[CONF_BINARY_ATTRS])
|
||||
attr_list = zigbee_new_attr_list(
|
||||
config[CONF_BINARY_INPUT_ATTRIB_LIST],
|
||||
zigbee_assign(binary_attrs.out_of_service, 0),
|
||||
zigbee_assign(binary_attrs.present_value, 0),
|
||||
zigbee_assign(binary_attrs.status_flags, 0),
|
||||
zigbee_set_string(binary_attrs.description, config[CONF_NAME]),
|
||||
)
|
||||
|
||||
cluster_id, clusters = zigbee_new_cluster_list(
|
||||
config, [ZigbeeClusterDesc(ZB_ZCL_CLUSTER_ID_BINARY_INPUT, attr_list)]
|
||||
)
|
||||
|
||||
zigbee_register_ep(config, cluster_id, 2, clusters)
|
||||
|
||||
var = await binary_sensor.new_binary_sensor(config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
template_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA], [], return_type=cg.optional.template(bool)
|
||||
)
|
||||
cg.add(var.set_template(template_))
|
||||
|
||||
cg.add(var.set_ep(config[KEY_EP_NUMBER]))
|
||||
cg.add(var.set_cluster_attributes(binary_attrs))
|
||||
hub = await cg.get_variable(config[CONF_ZIGBEE_ID])
|
||||
cg.add(var.set_parent(hub))
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"binary_sensor.zigbee.publish",
|
||||
binary_sensor.BinarySensorPublishAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(binary_sensor.BinarySensor),
|
||||
cv.Required(CONF_STATE): cv.templatable(cv.boolean),
|
||||
}
|
||||
),
|
||||
)
|
||||
async def binary_sensor_template_publish_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
template_ = await cg.templatable(config[CONF_STATE], args, bool)
|
||||
cg.add(var.set_state(template_))
|
||||
return var
|
@ -0,0 +1,56 @@
|
||||
#include "zigbee_binary_sensor.h"
|
||||
#ifdef USE_ZIGBEE
|
||||
#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 {
|
||||
namespace zigbee {
|
||||
|
||||
static const char *const TAG = "zigbee.binary_sensor";
|
||||
|
||||
void ZigbeeBinarySensor::setup() {
|
||||
add_on_state_callback([this](bool state) {
|
||||
if (state) {
|
||||
cluster_attributes_->present_value = 1;
|
||||
} else {
|
||||
cluster_attributes_->present_value = 0;
|
||||
}
|
||||
ESP_LOGD(TAG, "set attribute ep: %d, present_value %d", ep_, cluster_attributes_->present_value);
|
||||
ZB_ZCL_SET_ATTRIBUTE(ep_, ZB_ZCL_CLUSTER_ID_BINARY_INPUT, ZB_ZCL_CLUSTER_SERVER_ROLE,
|
||||
ZB_ZCL_ATTR_BINARY_INPUT_PRESENT_VALUE_ID, &cluster_attributes_->present_value, ZB_FALSE);
|
||||
this->parent_->flush();
|
||||
});
|
||||
|
||||
if (!this->publish_initial_state_)
|
||||
return;
|
||||
|
||||
if (this->f_ != nullptr) {
|
||||
this->publish_initial_state(this->f_().value_or(false));
|
||||
} else {
|
||||
this->publish_initial_state(false);
|
||||
}
|
||||
}
|
||||
|
||||
void ZigbeeBinarySensor::loop() {
|
||||
if (this->f_ == nullptr)
|
||||
return;
|
||||
|
||||
auto s = this->f_();
|
||||
if (s.has_value()) {
|
||||
this->publish_state(*s);
|
||||
}
|
||||
}
|
||||
|
||||
void ZigbeeBinarySensor::dump_config() {
|
||||
LOG_BINARY_SENSOR("", "Zigbee Binary Sensor", this);
|
||||
ESP_LOGCONFIG(TAG, " EP: %d", ep_);
|
||||
}
|
||||
|
||||
} // namespace zigbee
|
||||
} // namespace esphome
|
||||
#endif
|
@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/zigbee/zigbee_component.h"
|
||||
#ifdef USE_ZIGBEE
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/binary_sensor/binary_sensor.h"
|
||||
extern "C" {
|
||||
#include <zboss_api.h>
|
||||
#include <zboss_api_addons.h>
|
||||
}
|
||||
|
||||
#define ZB_SET_ATTR_DESCR_WITH_ZB_ZCL_ATTR_BINARY_INPUT_DESCRIPTION_ID(data_ptr) \
|
||||
{ \
|
||||
ZB_ZCL_ATTR_BINARY_INPUT_DESCRIPTION_ID, ZB_ZCL_ATTR_TYPE_CHAR_STRING, ZB_ZCL_ATTR_ACCESS_READ_ONLY, \
|
||||
(ZB_ZCL_NON_MANUFACTURER_SPECIFIC), (void *) (data_ptr) \
|
||||
}
|
||||
|
||||
#define ESPHOME_ZB_ZCL_DECLARE_BINARY_INPUT_ATTRIB_LIST(attr_list, out_of_service, present_value, status_flag, \
|
||||
description) \
|
||||
ZB_ZCL_START_DECLARE_ATTRIB_LIST_CLUSTER_REVISION(attr_list, ZB_ZCL_BINARY_INPUT) \
|
||||
ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_BINARY_INPUT_OUT_OF_SERVICE_ID, (out_of_service)) \
|
||||
ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_BINARY_INPUT_PRESENT_VALUE_ID, (present_value)) \
|
||||
ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_BINARY_INPUT_STATUS_FLAG_ID, (status_flag)) \
|
||||
ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_BINARY_INPUT_DESCRIPTION_ID, (description)) \
|
||||
ZB_ZCL_FINISH_DECLARE_ATTRIB_LIST
|
||||
|
||||
namespace esphome {
|
||||
namespace zigbee {
|
||||
|
||||
class ZigbeeBinarySensor : public ZigbeeEntity, public binary_sensor::BinarySensor, public Component {
|
||||
public:
|
||||
void set_template(std::function<optional<bool>()> &&f) { this->f_ = f; }
|
||||
void set_cluster_attributes(BinaryAttrs &cluster_attributes) { this->cluster_attributes_ = &cluster_attributes; }
|
||||
|
||||
void setup() override;
|
||||
void loop() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
|
||||
protected:
|
||||
std::function<optional<bool>()> f_{nullptr};
|
||||
BinaryAttrs *cluster_attributes_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace zigbee
|
||||
} // namespace esphome
|
||||
#endif
|
70
esphome/components/zigbee/const.py
Normal file
70
esphome/components/zigbee/const.py
Normal file
@ -0,0 +1,70 @@
|
||||
import esphome.codegen as cg
|
||||
|
||||
zigbee_ns = cg.esphome_ns.namespace("zigbee")
|
||||
Zigbee = zigbee_ns.class_("Zigbee", cg.Component)
|
||||
|
||||
zb_char_t_ptr = cg.global_ns.namespace("zb_char_t *")
|
||||
|
||||
CONF_ZIGBEE_ID = "zigbee_id"
|
||||
CONF_SWITCH = "switch"
|
||||
CONF_MAX_EP_NUMBER = 8
|
||||
CONF_EP = "ep"
|
||||
|
||||
zb_zcl_basic_attrs_ext_t = cg.global_ns.namespace("zb_zcl_basic_attrs_ext_t")
|
||||
zb_zcl_identify_attrs_t = cg.global_ns.namespace("zb_zcl_identify_attrs_t")
|
||||
zb_zcl_groups_attrs_t = cg.global_ns.namespace("zb_zcl_groups_attrs_t")
|
||||
zb_zcl_scenes_attrs_t = cg.global_ns.namespace("zb_zcl_scenes_attrs_t")
|
||||
|
||||
CONF_BASIC_ATTRS_EXT = "basic_attrs_ext"
|
||||
CONF_IDENTIFY_ATTRS = "identify_attrs"
|
||||
CONF_GROUPS_ATTRS = "groups_attrs"
|
||||
CONF_SCENES_ATTRS = "scenes_attrs"
|
||||
CONF_BINARY_ATTRS = "binary_attrs"
|
||||
CONF_TIME_ATTRS = "time_attrs"
|
||||
|
||||
CONF_BASIC_ATTRIB_LIST_EXT = "basic_attrib_list_ext"
|
||||
CONF_IDENTIFY_ATTRIB_LIST = "identify_attrib_list"
|
||||
CONF_GROUPS_ATTRIB_LIST = "groups_attrib_list"
|
||||
CONF_SCENES_ATTRIB_LIST = "scenes_attrib_list"
|
||||
|
||||
|
||||
# it has to be class to make use_id work
|
||||
ZB_ZCL_DECLARE_BASIC_ATTRIB_LIST_EXT = cg.global_ns.class_(
|
||||
"ZB_ZCL_DECLARE_BASIC_ATTRIB_LIST_EXT"
|
||||
)
|
||||
ZB_ZCL_DECLARE_IDENTIFY_ATTRIB_LIST = cg.global_ns.class_(
|
||||
"ZB_ZCL_DECLARE_IDENTIFY_ATTRIB_LIST"
|
||||
)
|
||||
ZB_ZCL_DECLARE_GROUPS_ATTRIB_LIST = cg.global_ns.class_(
|
||||
"ZB_ZCL_DECLARE_GROUPS_ATTRIB_LIST"
|
||||
)
|
||||
ZB_ZCL_DECLARE_SCENES_ATTRIB_LIST = cg.global_ns.class_(
|
||||
"ZB_ZCL_DECLARE_SCENES_ATTRIB_LIST"
|
||||
)
|
||||
|
||||
# input/output
|
||||
BinaryAttrs = zigbee_ns.struct("BinaryAttrs")
|
||||
|
||||
# input
|
||||
CONF_BINARY_INPUT_ATTRIB_LIST = "binary_input_attrib_list"
|
||||
|
||||
# output
|
||||
CONF_BINARY_OUTPUT_ATTRIB_LIST = "binary_output_attrib_list"
|
||||
|
||||
# time
|
||||
CONF_TIME_ATTRIB_LIST = "time_attrib_list"
|
||||
|
||||
# general
|
||||
ESPHOME_ZB_HA_DECLARE_EP = cg.global_ns.namespace("ESPHOME_ZB_HA_DECLARE_EP")
|
||||
CONF_CLUSTER_LIST = "cluster_list"
|
||||
|
||||
|
||||
# clusters
|
||||
ZB_ZCL_CLUSTER_ID_BASIC = "ZB_ZCL_CLUSTER_ID_BASIC"
|
||||
ZB_ZCL_CLUSTER_ID_IDENTIFY = "ZB_ZCL_CLUSTER_ID_IDENTIFY"
|
||||
ZB_ZCL_CLUSTER_ID_GROUPS = "ZB_ZCL_CLUSTER_ID_GROUPS"
|
||||
ZB_ZCL_CLUSTER_ID_SCENES = "ZB_ZCL_CLUSTER_ID_SCENES"
|
||||
|
||||
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_TIME = "ZB_ZCL_CLUSTER_ID_TIME"
|
97
esphome/components/zigbee/switch/__init__.py
Normal file
97
esphome/components/zigbee/switch/__init__.py
Normal file
@ -0,0 +1,97 @@
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import output, switch
|
||||
from esphome.components.zigbee_ctx import (
|
||||
KEY_EP_NUMBER,
|
||||
consume_ep_slots,
|
||||
zigbee_register_ep,
|
||||
)
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID, CONF_NAME, CONF_OUTPUT, CONF_STATE
|
||||
from esphome.core import coroutine_with_priority
|
||||
|
||||
from .. import (
|
||||
ZigbeeBaseSchema,
|
||||
ZigbeeClusterDesc,
|
||||
zigbee_assign,
|
||||
zigbee_new_attr_list,
|
||||
zigbee_new_cluster_list,
|
||||
zigbee_new_variable,
|
||||
zigbee_set_string,
|
||||
)
|
||||
from ..const import (
|
||||
CONF_BINARY_ATTRS,
|
||||
CONF_BINARY_OUTPUT_ATTRIB_LIST,
|
||||
CONF_ZIGBEE_ID,
|
||||
ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT,
|
||||
BinaryAttrs,
|
||||
zigbee_ns,
|
||||
)
|
||||
|
||||
AUTO_LOAD = ["zigbee"]
|
||||
|
||||
ZigbeeSwitch = zigbee_ns.class_("ZigbeeSwitch", switch.Switch, cg.Component)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
switch.switch_schema(ZigbeeSwitch)
|
||||
.extend(
|
||||
{
|
||||
cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput),
|
||||
cv.GenerateID(CONF_BINARY_ATTRS): cv.declare_id(BinaryAttrs),
|
||||
cv.GenerateID(CONF_BINARY_OUTPUT_ATTRIB_LIST): cv.declare_id(
|
||||
cg.global_ns.namespace(
|
||||
"ESPHOME_ZB_ZCL_DECLARE_BINARY_OUTPUT_ATTRIB_LIST"
|
||||
)
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
.extend(ZigbeeBaseSchema),
|
||||
consume_ep_slots,
|
||||
)
|
||||
|
||||
|
||||
@coroutine_with_priority(50.0)
|
||||
async def to_code(config):
|
||||
binary_attrs = zigbee_new_variable(config[CONF_BINARY_ATTRS])
|
||||
attr_list = zigbee_new_attr_list(
|
||||
config[CONF_BINARY_OUTPUT_ATTRIB_LIST],
|
||||
zigbee_assign(binary_attrs.out_of_service, 0),
|
||||
zigbee_assign(binary_attrs.present_value, 0),
|
||||
zigbee_assign(binary_attrs.status_flags, 0),
|
||||
zigbee_set_string(binary_attrs.description, config[CONF_NAME]),
|
||||
)
|
||||
|
||||
cluster_id, clusters = zigbee_new_cluster_list(
|
||||
config, [ZigbeeClusterDesc(ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT, attr_list)]
|
||||
)
|
||||
|
||||
zigbee_register_ep(config, cluster_id, 2, clusters)
|
||||
|
||||
var = await switch.new_switch(config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
output_ = await cg.get_variable(config[CONF_OUTPUT])
|
||||
cg.add(var.set_output(output_))
|
||||
cg.add(var.set_ep(config[KEY_EP_NUMBER]))
|
||||
cg.add(var.set_cluster_attributes(binary_attrs))
|
||||
hub = await cg.get_variable(config[CONF_ZIGBEE_ID])
|
||||
cg.add(var.set_parent(hub))
|
||||
|
||||
|
||||
@automation.register_action(
|
||||
"switch.zigbee.publish",
|
||||
switch.SwitchPublishAction,
|
||||
cv.Schema(
|
||||
{
|
||||
cv.Required(CONF_ID): cv.use_id(switch.Switch),
|
||||
cv.Required(CONF_STATE): cv.templatable(cv.boolean),
|
||||
}
|
||||
),
|
||||
)
|
||||
async def switch_template_publish_to_code(config, action_id, template_arg, args):
|
||||
paren = await cg.get_variable(config[CONF_ID])
|
||||
var = cg.new_Pvariable(action_id, template_arg, paren)
|
||||
template_ = await cg.templatable(config[CONF_STATE], args, bool)
|
||||
cg.add(var.set_state(template_))
|
||||
return var
|
47
esphome/components/zigbee/switch/zcl_binary_output.cpp
Normal file
47
esphome/components/zigbee/switch/zcl_binary_output.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
#include "esphome/core/defines.h"
|
||||
#ifdef USE_ZIGBEE
|
||||
extern "C" {
|
||||
#include "zboss_api.h"
|
||||
#include "zcl/zb_zcl_common.h"
|
||||
}
|
||||
#include "zigbee_switch.h"
|
||||
|
||||
static zb_ret_t check_value_binary_output_server(zb_uint16_t attr_id, zb_uint8_t endpoint, zb_uint8_t *value);
|
||||
|
||||
void zb_zcl_binary_output_init_server(void) {
|
||||
zb_zcl_add_cluster_handlers(ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT, ZB_ZCL_CLUSTER_SERVER_ROLE,
|
||||
check_value_binary_output_server, (zb_zcl_cluster_write_attr_hook_t) NULL,
|
||||
(zb_zcl_cluster_handler_t) NULL);
|
||||
}
|
||||
|
||||
void zb_zcl_binary_output_init_client(void) {
|
||||
zb_zcl_add_cluster_handlers(ZB_ZCL_CLUSTER_ID_BINARY_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);
|
||||
}
|
||||
|
||||
#define ZB_ZCL_BINARY_OUTPUT_STATUS_FLAG_MAX_VALUE 0x0F
|
||||
|
||||
static zb_ret_t check_value_binary_output_server(zb_uint16_t attr_id, zb_uint8_t endpoint, zb_uint8_t *value) {
|
||||
zb_ret_t ret = RET_OK;
|
||||
ZVUNUSED(endpoint);
|
||||
|
||||
switch (attr_id) {
|
||||
case ZB_ZCL_ATTR_BINARY_OUTPUT_OUT_OF_SERVICE_ID:
|
||||
case ZB_ZCL_ATTR_BINARY_OUTPUT_PRESENT_VALUE_ID:
|
||||
ret = ZB_ZCL_CHECK_BOOL_VALUE(*value) ? RET_OK : RET_ERROR;
|
||||
break;
|
||||
|
||||
case ZB_ZCL_ATTR_BINARY_OUTPUT_STATUS_FLAG_ID:
|
||||
if (*value > ZB_ZCL_BINARY_OUTPUT_STATUS_FLAG_MAX_VALUE) {
|
||||
ret = RET_ERROR;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
89
esphome/components/zigbee/switch/zigbee_switch.cpp
Normal file
89
esphome/components/zigbee/switch/zigbee_switch.cpp
Normal file
@ -0,0 +1,89 @@
|
||||
#include "zigbee_switch.h"
|
||||
#ifdef USE_ZIGBEE
|
||||
#include "esphome/core/log.h"
|
||||
#include <zephyr/settings/settings.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 {
|
||||
namespace zigbee {
|
||||
|
||||
static const char *const TAG = "zigbee_on_off.switch";
|
||||
|
||||
void ZigbeeSwitch::dump_config() {
|
||||
LOG_SWITCH("", "Zigbee Switch", this);
|
||||
ESP_LOGCONFIG(TAG, " EP: %d", ep_);
|
||||
}
|
||||
|
||||
void ZigbeeSwitch::setup() {
|
||||
this->parent_->add_callback(this->ep_, [this](zb_bufid_t bufid) { this->zcl_device_cb_(bufid); });
|
||||
add_on_state_callback([this](bool state) {
|
||||
if (state) {
|
||||
cluster_attributes_->present_value = 1;
|
||||
} else {
|
||||
cluster_attributes_->present_value = 0;
|
||||
}
|
||||
ESP_LOGD(TAG, "set attribute ep: %d, present_value %d", ep_, cluster_attributes_->present_value);
|
||||
ZB_ZCL_SET_ATTRIBUTE(ep_, ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT, ZB_ZCL_CLUSTER_SERVER_ROLE,
|
||||
ZB_ZCL_ATTR_BINARY_OUTPUT_PRESENT_VALUE_ID, &cluster_attributes_->present_value, ZB_FALSE);
|
||||
this->parent_->flush();
|
||||
});
|
||||
|
||||
bool initial_state = this->get_initial_state_with_restore_mode().value_or(false);
|
||||
|
||||
if (initial_state) {
|
||||
this->turn_on();
|
||||
} else {
|
||||
this->turn_off();
|
||||
}
|
||||
}
|
||||
|
||||
void ZigbeeSwitch::write_state(bool state) {
|
||||
if (state) {
|
||||
this->output_->turn_on();
|
||||
} else {
|
||||
this->output_->turn_off();
|
||||
}
|
||||
this->publish_state(state);
|
||||
}
|
||||
|
||||
void ZigbeeSwitch::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;
|
||||
|
||||
p_device_cb_param->status = RET_OK;
|
||||
|
||||
switch (device_cb_id) {
|
||||
/* ZCL set attribute value */
|
||||
case ZB_ZCL_SET_ATTR_VALUE_CB_ID:
|
||||
if (cluster_id == ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT) {
|
||||
uint8_t value = p_device_cb_param->cb_param.set_attr_value_param.values.data8;
|
||||
ESP_LOGI(TAG, "binary output attribute setting to %hd", value);
|
||||
|
||||
if (attr_id == ZB_ZCL_ATTR_BINARY_OUTPUT_PRESENT_VALUE_ID) {
|
||||
this->parent_->schedule([this, value]() { write_state((zb_bool_t) value); });
|
||||
}
|
||||
} else {
|
||||
/* other clusters attribute handled here */
|
||||
ESP_LOGI(TAG, "Unhandled cluster attribute id: %d", cluster_id);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
p_device_cb_param->status = RET_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "%s status: %hd", __func__, p_device_cb_param->status);
|
||||
}
|
||||
|
||||
} // namespace zigbee
|
||||
} // namespace esphome
|
||||
#endif
|
88
esphome/components/zigbee/switch/zigbee_switch.h
Normal file
88
esphome/components/zigbee/switch/zigbee_switch.h
Normal file
@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/zigbee/zigbee_component.h"
|
||||
#ifdef USE_ZIGBEE
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/switch/switch.h"
|
||||
#include "esphome/components/output/binary_output.h"
|
||||
extern "C" {
|
||||
#include <zboss_api.h>
|
||||
#include <zboss_api_addons.h>
|
||||
}
|
||||
|
||||
#define ZB_ZCL_BINARY_OUTPUT_CLUSTER_REVISION_DEFAULT ((zb_uint16_t) 0x0001u)
|
||||
|
||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||
enum zb_zcl_binary_output_attr_e {
|
||||
ZB_ZCL_ATTR_BINARY_OUTPUT_DESCRIPTION_ID = 0x001C,
|
||||
ZB_ZCL_ATTR_BINARY_OUTPUT_OUT_OF_SERVICE_ID = 0x0051,
|
||||
ZB_ZCL_ATTR_BINARY_OUTPUT_PRESENT_VALUE_ID = 0x0055,
|
||||
ZB_ZCL_ATTR_BINARY_OUTPUT_STATUS_FLAG_ID = 0x006F,
|
||||
};
|
||||
|
||||
#define ZB_SET_ATTR_DESCR_WITH_ZB_ZCL_ATTR_BINARY_OUTPUT_OUT_OF_SERVICE_ID(data_ptr) \
|
||||
{ \
|
||||
ZB_ZCL_ATTR_BINARY_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) \
|
||||
}
|
||||
|
||||
#define ZB_SET_ATTR_DESCR_WITH_ZB_ZCL_ATTR_BINARY_OUTPUT_PRESENT_VALUE_ID(data_ptr) \
|
||||
{ \
|
||||
ZB_ZCL_ATTR_BINARY_OUTPUT_PRESENT_VALUE_ID, ZB_ZCL_ATTR_TYPE_BOOL, \
|
||||
ZB_ZCL_ATTR_ACCESS_READ_WRITE | ZB_ZCL_ATTR_ACCESS_REPORTING, (ZB_ZCL_NON_MANUFACTURER_SPECIFIC), \
|
||||
(void *) (data_ptr) \
|
||||
}
|
||||
|
||||
#define ZB_SET_ATTR_DESCR_WITH_ZB_ZCL_ATTR_BINARY_OUTPUT_STATUS_FLAG_ID(data_ptr) \
|
||||
{ \
|
||||
ZB_ZCL_ATTR_BINARY_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_BINARY_OUTPUT_DESCRIPTION_ID(data_ptr) \
|
||||
{ \
|
||||
ZB_ZCL_ATTR_BINARY_OUTPUT_DESCRIPTION_ID, ZB_ZCL_ATTR_TYPE_CHAR_STRING, ZB_ZCL_ATTR_ACCESS_READ_ONLY, \
|
||||
(ZB_ZCL_NON_MANUFACTURER_SPECIFIC), (void *) (data_ptr) \
|
||||
}
|
||||
|
||||
#define ESPHOME_ZB_ZCL_DECLARE_BINARY_OUTPUT_ATTRIB_LIST(attr_list, out_of_service, present_value, status_flag, \
|
||||
description) \
|
||||
ZB_ZCL_START_DECLARE_ATTRIB_LIST_CLUSTER_REVISION(attr_list, ZB_ZCL_BINARY_OUTPUT) \
|
||||
ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_BINARY_OUTPUT_OUT_OF_SERVICE_ID, (out_of_service)) \
|
||||
ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_BINARY_OUTPUT_PRESENT_VALUE_ID, (present_value)) \
|
||||
ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_BINARY_OUTPUT_STATUS_FLAG_ID, (status_flag)) \
|
||||
ZB_ZCL_SET_ATTR_DESC(ZB_ZCL_ATTR_BINARY_OUTPUT_DESCRIPTION_ID, (description)) \
|
||||
ZB_ZCL_FINISH_DECLARE_ATTRIB_LIST
|
||||
|
||||
void zb_zcl_binary_output_init_server();
|
||||
void zb_zcl_binary_output_init_client();
|
||||
|
||||
#define ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT_SERVER_ROLE_INIT zb_zcl_binary_output_init_server
|
||||
#define ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT_CLIENT_ROLE_INIT zb_zcl_binary_output_init_client
|
||||
|
||||
namespace esphome {
|
||||
namespace zigbee {
|
||||
|
||||
class ZigbeeSwitch : public switch_::Switch, public ZigbeeEntity, public Component {
|
||||
public:
|
||||
void set_output(output::BinaryOutput *output) { this->output_ = output; }
|
||||
void set_cluster_attributes(BinaryAttrs &cluster_attributes) { this->cluster_attributes_ = &cluster_attributes; }
|
||||
|
||||
void setup() override;
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE - 1.0f; }
|
||||
|
||||
void dump_config() override;
|
||||
|
||||
protected:
|
||||
void write_state(bool state) override;
|
||||
void zcl_device_cb_(zb_bufid_t bufid);
|
||||
|
||||
output::BinaryOutput *output_;
|
||||
BinaryAttrs *cluster_attributes_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace zigbee
|
||||
} // namespace esphome
|
||||
#endif
|
74
esphome/components/zigbee/time/__init__.py
Normal file
74
esphome/components/zigbee/time/__init__.py
Normal file
@ -0,0 +1,74 @@
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import time as time_
|
||||
from esphome.components.zigbee_ctx import (
|
||||
KEY_EP_NUMBER,
|
||||
consume_ep_slots,
|
||||
zigbee_register_ep,
|
||||
)
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import CONF_ID
|
||||
from esphome.core import coroutine_with_priority
|
||||
|
||||
from .. import (
|
||||
ZigbeeBaseSchema,
|
||||
ZigbeeClusterDesc,
|
||||
zigbee_new_attr_list,
|
||||
zigbee_new_cluster_list,
|
||||
zigbee_new_variable,
|
||||
)
|
||||
from ..const import (
|
||||
CONF_TIME_ATTRIB_LIST,
|
||||
CONF_TIME_ATTRS,
|
||||
CONF_ZIGBEE_ID,
|
||||
ZB_ZCL_CLUSTER_ID_TIME,
|
||||
zigbee_ns,
|
||||
)
|
||||
|
||||
AUTO_LOAD = ["zigbee"]
|
||||
|
||||
ZigbeeTime = zigbee_ns.class_("ZigbeeTime", time_.RealTimeClock)
|
||||
|
||||
CONFIG_SCHEMA = cv.All(
|
||||
time_.TIME_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(ZigbeeTime),
|
||||
cv.GenerateID(CONF_TIME_ATTRS): cv.declare_id(
|
||||
cg.global_ns.struct("zb_zcl_time_attrs_t")
|
||||
),
|
||||
cv.GenerateID(CONF_TIME_ATTRIB_LIST): cv.declare_id(
|
||||
cg.global_ns.namespace("ZB_ZCL_DECLARE_TIME_ATTR_LIST")
|
||||
),
|
||||
}
|
||||
)
|
||||
.extend(cv.COMPONENT_SCHEMA)
|
||||
.extend(ZigbeeBaseSchema)
|
||||
.extend(cv.polling_component_schema("1s")),
|
||||
consume_ep_slots,
|
||||
)
|
||||
|
||||
|
||||
@coroutine_with_priority(50.0)
|
||||
async def to_code(config):
|
||||
time_attrs = zigbee_new_variable(config[CONF_TIME_ATTRS])
|
||||
attr_list = zigbee_new_attr_list(
|
||||
config[CONF_TIME_ATTRIB_LIST],
|
||||
time_attrs,
|
||||
)
|
||||
cluster_id, clusters = zigbee_new_cluster_list(
|
||||
config,
|
||||
[
|
||||
ZigbeeClusterDesc(ZB_ZCL_CLUSTER_ID_TIME, attr_list),
|
||||
ZigbeeClusterDesc(ZB_ZCL_CLUSTER_ID_TIME),
|
||||
],
|
||||
)
|
||||
|
||||
zigbee_register_ep(config, cluster_id, 0, clusters)
|
||||
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await time_.register_time(var, config)
|
||||
await cg.register_component(var, config)
|
||||
|
||||
cg.add(var.set_ep(config[KEY_EP_NUMBER]))
|
||||
cg.add(var.set_cluster_attributes(time_attrs))
|
||||
hub = await cg.get_variable(config[CONF_ZIGBEE_ID])
|
||||
cg.add(var.set_parent(hub))
|
87
esphome/components/zigbee/time/zigbee_time.cpp
Normal file
87
esphome/components/zigbee/time/zigbee_time.cpp
Normal file
@ -0,0 +1,87 @@
|
||||
#include "zigbee_time.h"
|
||||
#ifdef USE_ZIGBEE
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace zigbee {
|
||||
|
||||
static const char *const TAG = "zigbee.time";
|
||||
|
||||
// This time standard is the number of
|
||||
// seconds since 0 hrs 0 mins 0 sec on 1st January 2000 UTC (Universal Coordinated Time).
|
||||
constexpr time_t EPOCH_2000 = 946684800;
|
||||
|
||||
ZigbeeTime *global_time = nullptr;
|
||||
|
||||
void zb_zcl_time_sync_time_server_found_cb(zb_ret_t status, zb_uint32_t auth_level, zb_uint16_t short_addr,
|
||||
zb_uint8_t ep, zb_uint32_t nw_time) {
|
||||
if (status == RET_OK && auth_level >= ZB_ZCL_TIME_HAS_SYNCHRONIZED_BIT) {
|
||||
global_time->set_epoch_time(nw_time + EPOCH_2000);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "status: %d, auth_level: %u, short_addr: %d, ep: %d, nw_time: %u", status, auth_level, short_addr, ep,
|
||||
nw_time);
|
||||
}
|
||||
}
|
||||
|
||||
void ZigbeeTime::setup() {
|
||||
global_time = this;
|
||||
this->parent_->add_callback(this->ep_, [this](zb_bufid_t bufid) { this->zcl_device_cb_(bufid); });
|
||||
synchronize_epoch_(EPOCH_2000);
|
||||
parent_->add_join_callback([this]() { zb_zcl_time_server_synchronize(ep_, zb_zcl_time_sync_time_server_found_cb); });
|
||||
}
|
||||
|
||||
void ZigbeeTime::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "Zigbee Time");
|
||||
ESP_LOGCONFIG(TAG, " EP: %d", ep_);
|
||||
}
|
||||
|
||||
void ZigbeeTime::update() {
|
||||
time_t time = timestamp_now();
|
||||
cluster_attributes_->time = time - EPOCH_2000;
|
||||
}
|
||||
|
||||
void ZigbeeTime::set_epoch_time(uint32_t epoch) {
|
||||
this->parent_->schedule([this, epoch]() {
|
||||
this->synchronize_epoch_(epoch);
|
||||
has_time_ = true;
|
||||
});
|
||||
}
|
||||
|
||||
void ZigbeeTime::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;
|
||||
|
||||
p_device_cb_param->status = RET_OK;
|
||||
|
||||
switch (device_cb_id) {
|
||||
/* ZCL set attribute value */
|
||||
case ZB_ZCL_SET_ATTR_VALUE_CB_ID:
|
||||
if (cluster_id == ZB_ZCL_CLUSTER_ID_TIME) {
|
||||
if (attr_id == ZB_ZCL_ATTR_TIME_TIME_ID) {
|
||||
zb_uint32_t value = p_device_cb_param->cb_param.set_attr_value_param.values.data32;
|
||||
ESP_LOGI(TAG, "binary output attribute setting to %u", value);
|
||||
synchronize_epoch_(value + EPOCH_2000);
|
||||
} else if (attr_id == ZB_ZCL_ATTR_TIME_TIME_STATUS_ID) {
|
||||
zb_uint8_t value = p_device_cb_param->cb_param.set_attr_value_param.values.data8;
|
||||
ESP_LOGI(TAG, "binary output attribute setting to %hd", value);
|
||||
has_time_ = ZB_ZCL_TIME_TIME_STATUS_SYNCHRONIZED_BIT_IS_SET(value);
|
||||
}
|
||||
} else {
|
||||
/* other clusters attribute handled here */
|
||||
ESP_LOGI(TAG, "Unhandled cluster attribute id: %d", cluster_id);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
p_device_cb_param->status = RET_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "%s status: %hd", __func__, p_device_cb_param->status);
|
||||
}
|
||||
|
||||
} // namespace zigbee
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
39
esphome/components/zigbee/time/zigbee_time.h
Normal file
39
esphome/components/zigbee/time/zigbee_time.h
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/zigbee/zigbee_component.h"
|
||||
#ifdef USE_ZIGBEE
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/time/real_time_clock.h"
|
||||
|
||||
extern "C" {
|
||||
#include <zboss_api.h>
|
||||
#include <zboss_api_addons.h>
|
||||
}
|
||||
|
||||
namespace esphome {
|
||||
namespace zigbee {
|
||||
|
||||
class ZigbeeTime : public time::RealTimeClock, public ZigbeeEntity {
|
||||
public:
|
||||
void setup() override;
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE - 1.0f; }
|
||||
void dump_config() override;
|
||||
void update() override;
|
||||
|
||||
void set_cluster_attributes(zb_zcl_time_attrs_t &cluster_attributes) {
|
||||
this->cluster_attributes_ = &cluster_attributes;
|
||||
}
|
||||
|
||||
void set_epoch_time(uint32_t epoch);
|
||||
|
||||
protected:
|
||||
void zcl_device_cb_(zb_bufid_t bufid);
|
||||
zb_zcl_time_attrs_t *cluster_attributes_{nullptr};
|
||||
|
||||
bool has_time_{false};
|
||||
};
|
||||
|
||||
} // namespace zigbee
|
||||
} // namespace esphome
|
||||
|
||||
#endif
|
202
esphome/components/zigbee/zigbee_component.cpp
Normal file
202
esphome/components/zigbee/zigbee_component.cpp
Normal file
@ -0,0 +1,202 @@
|
||||
#include "zigbee_component.h"
|
||||
#ifdef USE_ZIGBEE
|
||||
#include "esphome/core/log.h"
|
||||
#include <zephyr/settings/settings.h>
|
||||
#include <zephyr/storage/flash_map.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 {
|
||||
namespace zigbee {
|
||||
|
||||
static const char *const TAG = "zigbee";
|
||||
|
||||
Zigbee *global_zigbee = nullptr;
|
||||
|
||||
#define IEEE_ADDR_BUF_SIZE 17
|
||||
|
||||
void Zigbee::zboss_signal_handler_esphome(zb_bufid_t bufid) {
|
||||
zb_zdo_app_signal_hdr_t *sig_hndler = NULL;
|
||||
zb_zdo_app_signal_type_t sig = zb_get_app_signal(bufid, &sig_hndler);
|
||||
zb_ret_t status = ZB_GET_APP_SIGNAL_STATUS(bufid);
|
||||
|
||||
switch (sig) {
|
||||
case ZB_ZDO_SIGNAL_SKIP_STARTUP:
|
||||
ESP_LOGD(TAG, "ZB_ZDO_SIGNAL_SKIP_STARTUP, status: %d", status);
|
||||
break;
|
||||
case ZB_ZDO_SIGNAL_PRODUCTION_CONFIG_READY:
|
||||
ESP_LOGD(TAG, "ZB_ZDO_SIGNAL_PRODUCTION_CONFIG_READY, status: %d", status);
|
||||
break;
|
||||
case ZB_ZDO_SIGNAL_LEAVE:
|
||||
ESP_LOGD(TAG, "ZB_ZDO_SIGNAL_LEAVE, status: %d", status);
|
||||
break;
|
||||
case ZB_BDB_SIGNAL_DEVICE_REBOOT:
|
||||
ESP_LOGD(TAG, "ZB_BDB_SIGNAL_DEVICE_REBOOT, status: %d", status);
|
||||
if (status == RET_OK) {
|
||||
on_join();
|
||||
}
|
||||
break;
|
||||
case ZB_BDB_SIGNAL_STEERING:
|
||||
break;
|
||||
case ZB_COMMON_SIGNAL_CAN_SLEEP:
|
||||
ESP_LOGV(TAG, "ZB_COMMON_SIGNAL_CAN_SLEEP, status: %d", status);
|
||||
break;
|
||||
case ZB_BDB_SIGNAL_DEVICE_FIRST_START:
|
||||
ESP_LOGD(TAG, "ZB_BDB_SIGNAL_DEVICE_FIRST_START, status: %d", status);
|
||||
break;
|
||||
case ZB_NLME_STATUS_INDICATION:
|
||||
ESP_LOGD(TAG, "ZB_NLME_STATUS_INDICATION, status: %d", status);
|
||||
break;
|
||||
case ZB_BDB_SIGNAL_TC_REJOIN_DONE:
|
||||
ESP_LOGD(TAG, "ZB_BDB_SIGNAL_TC_REJOIN_DONE, status: %d", status);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGD(TAG, "zboss_signal_handler sig: %d, status: %d", sig, status);
|
||||
break;
|
||||
}
|
||||
|
||||
auto err = zigbee_default_signal_handler(bufid);
|
||||
if (err != RET_OK) {
|
||||
ESP_LOGE(TAG, "zigbee_default_signal_handler ERROR %u [%s]", err, zb_error_to_string_get(err));
|
||||
}
|
||||
|
||||
switch (sig) {
|
||||
case ZB_BDB_SIGNAL_STEERING:
|
||||
ESP_LOGD(TAG, "ZB_BDB_SIGNAL_STEERING, status: %d", status);
|
||||
if (status == RET_OK) {
|
||||
zb_ext_pan_id_t extended_pan_id;
|
||||
char ieee_addr_buf[IEEE_ADDR_BUF_SIZE] = {0};
|
||||
int addr_len;
|
||||
|
||||
zb_get_extended_pan_id(extended_pan_id);
|
||||
addr_len = ieee_addr_to_str(ieee_addr_buf, sizeof(ieee_addr_buf), extended_pan_id);
|
||||
|
||||
for (int i = 0; i < addr_len; ++i) {
|
||||
if (ieee_addr_buf[i] != '0') {
|
||||
on_join();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* All callbacks should either reuse or free passed buffers.
|
||||
* If bufid == 0, the buffer is invalid (not passed).
|
||||
*/
|
||||
if (bufid) {
|
||||
zb_buf_free(bufid);
|
||||
}
|
||||
}
|
||||
|
||||
void Zigbee::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;
|
||||
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,
|
||||
attr_id, endpoint);
|
||||
|
||||
auto cb = global_zigbee->callbacks_.find(endpoint);
|
||||
if (cb != global_zigbee->callbacks_.end()) {
|
||||
cb->second(bufid);
|
||||
return;
|
||||
}
|
||||
p_device_cb_param->status = RET_ERROR;
|
||||
}
|
||||
|
||||
void Zigbee::on_join() {
|
||||
this->schedule([this]() {
|
||||
ESP_LOGD(TAG, "joined the network");
|
||||
this->join_trigger_->trigger();
|
||||
if (this->join_cb_) {
|
||||
this->join_cb_();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _erase_flash(int area) {
|
||||
const struct flash_area *fap;
|
||||
flash_area_open(area, &fap);
|
||||
flash_area_erase(fap, 0, fap->fa_size);
|
||||
flash_area_close(fap);
|
||||
}
|
||||
|
||||
void Zigbee::setup() {
|
||||
global_zigbee = this;
|
||||
auto err = settings_subsys_init();
|
||||
if (err) {
|
||||
ESP_LOGE(TAG, "Failed to initialize settings subsystem, err: %d", err);
|
||||
return;
|
||||
}
|
||||
|
||||
#if 0
|
||||
_erase_flash(FIXED_PARTITION_ID(ZBOSS_NVRAM));
|
||||
_erase_flash(FIXED_PARTITION_ID(ZBOSS_PRODUCT_CONFIG));
|
||||
_erase_flash(FIXED_PARTITION_ID(SETTINGS_STORAGE));
|
||||
#endif
|
||||
|
||||
/* Register callback for handling ZCL commands. */
|
||||
ZB_ZCL_REGISTER_DEVICE_CB(zcl_device_cb);
|
||||
|
||||
/* Settings should be loaded after zcl_scenes_init */
|
||||
err = settings_load();
|
||||
if (err) {
|
||||
ESP_LOGE(TAG, "Cannot load settings, err: %d", err);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Start Zigbee default thread */
|
||||
zigbee_enable();
|
||||
}
|
||||
|
||||
static void send_attribute_report(zb_bufid_t bufid, zb_uint16_t cmd_id) {
|
||||
ESP_LOGD(TAG, "force zboss scheduler to wake and send attribute report");
|
||||
zb_buf_free(bufid);
|
||||
}
|
||||
|
||||
void Zigbee::flush() { need_flush_ = true; }
|
||||
|
||||
void Zigbee::loop() {
|
||||
if (need_flush_) {
|
||||
need_flush_ = false;
|
||||
zb_buf_get_out_delayed_ext(send_attribute_report, 0, 0);
|
||||
}
|
||||
std::function<void()> fn;
|
||||
mutex_.lock();
|
||||
if (!to_schedule_.empty()) {
|
||||
fn = std::move(to_schedule_.front());
|
||||
to_schedule_.pop_front();
|
||||
}
|
||||
mutex_.unlock();
|
||||
if (fn) {
|
||||
fn();
|
||||
}
|
||||
}
|
||||
|
||||
void Zigbee::schedule(std::function<void()> &&f) {
|
||||
mutex_.lock();
|
||||
to_schedule_.push_back(std::move(f));
|
||||
mutex_.unlock();
|
||||
}
|
||||
|
||||
void Zigbee::factory_reset() {
|
||||
ESP_LOGD(TAG, "factory reset");
|
||||
ZB_SCHEDULE_APP_CALLBACK(zb_bdb_reset_via_local_action, 0);
|
||||
}
|
||||
|
||||
} // namespace zigbee
|
||||
} // namespace esphome
|
||||
|
||||
extern "C" void zboss_signal_handler(zb_bufid_t bufid) {
|
||||
esphome::zigbee::global_zigbee->zboss_signal_handler_esphome(bufid);
|
||||
}
|
||||
#endif
|
99
esphome/components/zigbee/zigbee_component.h
Normal file
99
esphome/components/zigbee/zigbee_component.h
Normal file
@ -0,0 +1,99 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include "esphome/core/defines.h"
|
||||
#ifdef USE_ZIGBEE
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/automation.h"
|
||||
extern "C" {
|
||||
#include <zboss_api.h>
|
||||
#include <zboss_api_addons.h>
|
||||
}
|
||||
|
||||
#include <deque>
|
||||
|
||||
#define ESPHOME_ZB_DECLARE_SIMPLE_DESC(ep_name, in_clusters_count, out_clusters_count) \
|
||||
typedef ZB_PACKED_PRE struct zb_af_simple_desc_##ep_name##_##out_clusters_count##_##out_clusters_count##_s { \
|
||||
zb_uint8_t endpoint; /* Endpoint */ \
|
||||
zb_uint16_t app_profile_id; /* Application profile identifier */ \
|
||||
zb_uint16_t app_device_id; /* Application device identifier */ \
|
||||
zb_bitfield_t app_device_version : 4; /* Application device version */ \
|
||||
zb_bitfield_t reserved : 4; /* Reserved */ \
|
||||
zb_uint8_t app_input_cluster_count; /* Application input cluster count */ \
|
||||
zb_uint8_t app_output_cluster_count; /* Application output cluster count */ \
|
||||
/* Application input and output cluster list */ \
|
||||
zb_uint16_t app_cluster_list[(in_clusters_count) + (out_clusters_count)]; \
|
||||
} ZB_PACKED_STRUCT zb_af_simple_desc_##ep_name##_##in_clusters_count##_##out_clusters_count##_t
|
||||
|
||||
#define ESPHOME_CAT7(a, b, c, d, e, f, g) a##b##c##d##e##f##g
|
||||
#define ESPHOME_ZB_AF_SIMPLE_DESC_TYPE(ep_name, in_num, out_num) \
|
||||
ESPHOME_CAT7(zb_af_simple_desc_, ep_name, _, in_num, _, out_num, _t)
|
||||
|
||||
#define ESPHOME_ZB_ZCL_DECLARE_SIMPLE_DESC(ep_name, ep_id, 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) \
|
||||
simple_desc_##ep_name = {ep_id, ZB_AF_HA_PROFILE_ID, ZB_HA_CUSTOM_ATTR_DEVICE_ID, 0, 0, in_clust_num, \
|
||||
out_clust_num, {__VA_ARGS__}}
|
||||
|
||||
#define ESPHOME_ZB_HA_DECLARE_EP(ep_name, ep_id, cluster_list, in_cluster_num, out_cluster_num, report_attr_count, \
|
||||
...) \
|
||||
ESPHOME_ZB_ZCL_DECLARE_SIMPLE_DESC(ep_name, ep_id, in_cluster_num, out_cluster_num, __VA_ARGS__); \
|
||||
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_ZCL_ARRAY_SIZE(cluster_list, zb_zcl_cluster_desc_t), cluster_list, \
|
||||
(zb_af_simple_desc_1_1_t *) &simple_desc_##ep_name, report_attr_count, \
|
||||
reporting_info##ep_name, 0, NULL)
|
||||
|
||||
namespace esphome {
|
||||
namespace zigbee {
|
||||
|
||||
struct BinaryAttrs {
|
||||
zb_bool_t out_of_service;
|
||||
zb_bool_t present_value;
|
||||
zb_uint8_t status_flags;
|
||||
zb_uchar_t description[32]; // TODO it could be in progmem, max is ZB_ZCL_MAX_STRING_SIZE
|
||||
};
|
||||
|
||||
class Zigbee : public Component {
|
||||
public:
|
||||
void setup() override;
|
||||
void add_callback(zb_uint8_t endpoint, std::function<void(zb_bufid_t bufid)> cb) { this->callbacks_[endpoint] = cb; }
|
||||
void add_join_callback(std::function<void()> cb) { this->join_cb_ = cb; }
|
||||
void zboss_signal_handler_esphome(zb_bufid_t bufid);
|
||||
void factory_reset();
|
||||
Trigger<> *get_join_trigger() const { return this->join_trigger_; };
|
||||
void flush();
|
||||
void loop() override;
|
||||
|
||||
void schedule(std::function<void()> &&f);
|
||||
|
||||
protected:
|
||||
static void zcl_device_cb(zb_bufid_t bufid);
|
||||
void on_join();
|
||||
std::map<zb_uint8_t, std::function<void(zb_bufid_t bufid)>> callbacks_;
|
||||
std::function<void()> join_cb_;
|
||||
Trigger<> *join_trigger_{new Trigger<>()};
|
||||
bool need_flush_{false};
|
||||
std::deque<std::function<void()>> to_schedule_;
|
||||
Mutex mutex_;
|
||||
};
|
||||
|
||||
extern Zigbee *global_zigbee; // NOLINT(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
template<typename... Ts> class FactoryResetAction : public Action<Ts...> {
|
||||
public:
|
||||
void play(Ts... x) override { global_zigbee->factory_reset(); }
|
||||
};
|
||||
|
||||
class ZigbeeEntity {
|
||||
public:
|
||||
void set_parent(Zigbee *parent) { this->parent_ = parent; }
|
||||
void set_ep(zb_uint8_t ep) { this->ep_ = ep; }
|
||||
|
||||
protected:
|
||||
zb_uint8_t ep_{0};
|
||||
Zigbee *parent_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace zigbee
|
||||
} // namespace esphome
|
||||
#endif
|
62
esphome/components/zigbee_ctx/__init__.py
Normal file
62
esphome/components/zigbee_ctx/__init__.py
Normal file
@ -0,0 +1,62 @@
|
||||
from collections.abc import MutableMapping
|
||||
from typing import Any
|
||||
|
||||
import esphome.codegen as cg
|
||||
from esphome.components.zephyr import zephyr_add_pm_static
|
||||
from esphome.components.nrf52.boards import BOOTLOADER_CONFIG, Section
|
||||
from esphome.components.zephyr.const import KEY_BOOTLOADER, KEY_ZEPHYR
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
|
||||
KEY_ZIGBEE = "zigbee"
|
||||
KEY_EP_NUMBER = "ep_number"
|
||||
CONF_EP = "ep"
|
||||
|
||||
|
||||
def zigbee_set_core_data(config):
|
||||
if CORE.data[KEY_ZEPHYR][KEY_BOOTLOADER] in BOOTLOADER_CONFIG:
|
||||
zephyr_add_pm_static(
|
||||
[Section("empty_after_zboss_offset", 0xF4000, 0xC000, "flash_primary")]
|
||||
)
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def consume_ep_slots(config: MutableMapping) -> MutableMapping:
|
||||
data: dict[str, Any] = CORE.data.setdefault(KEY_ZIGBEE, {})
|
||||
slots: list[str] = data.setdefault(KEY_EP_NUMBER, [])
|
||||
slots.extend([""])
|
||||
config[KEY_EP_NUMBER] = len(CORE.data[KEY_ZIGBEE][KEY_EP_NUMBER])
|
||||
return config
|
||||
|
||||
|
||||
def zigbee_register_ep(
|
||||
config,
|
||||
cluster_id,
|
||||
report_attr_count: int,
|
||||
clusters,
|
||||
):
|
||||
id_ = config[CONF_EP]
|
||||
in_cluster_num = 0
|
||||
out_cluster_num = 0
|
||||
attrs = []
|
||||
for c in clusters:
|
||||
if c.attr:
|
||||
in_cluster_num += 1
|
||||
else:
|
||||
out_cluster_num += 1
|
||||
attrs.append(c.name)
|
||||
CORE.data[KEY_ZIGBEE][KEY_EP_NUMBER][config[KEY_EP_NUMBER] - 1] = str(id_)
|
||||
obj = cg.RawExpression(
|
||||
f"{id_.type}({id_}, {config[KEY_EP_NUMBER]}, {cluster_id}, {in_cluster_num}, {out_cluster_num}, {report_attr_count}, {', '.join(attrs)})"
|
||||
)
|
||||
CORE.add_global(obj)
|
||||
|
||||
|
||||
@coroutine_with_priority(10.0)
|
||||
async def to_code(config):
|
||||
cg.add_global(
|
||||
cg.RawExpression(
|
||||
f"ZBOSS_DECLARE_DEVICE_CTX_EP_VA(zb_device_ctx, &{', &'.join(CORE.data[KEY_ZIGBEE][KEY_EP_NUMBER])})"
|
||||
)
|
||||
)
|
||||
cg.add(cg.RawExpression("ZB_AF_REGISTER_DEVICE_CTX(&zb_device_ctx)"))
|
41
tests/components/zigbee/common.yaml
Normal file
41
tests/components/zigbee/common.yaml
Normal file
@ -0,0 +1,41 @@
|
||||
---
|
||||
switch:
|
||||
- platform: zigbee
|
||||
output: output_template
|
||||
id: zigbee_switch_1
|
||||
- platform: zigbee
|
||||
output: output_template
|
||||
id: zigbee_switch_2
|
||||
|
||||
output:
|
||||
- platform: template
|
||||
id: output_template
|
||||
type: binary
|
||||
write_action:
|
||||
- binary_sensor.zigbee.publish:
|
||||
id: zigbee_binary_sensor_1
|
||||
state: ON
|
||||
- binary_sensor.zigbee.publish:
|
||||
id: zigbee_binary_sensor_1
|
||||
state: OFF
|
||||
- platform: template
|
||||
id: output_factory
|
||||
type: binary
|
||||
write_action:
|
||||
- zigbee.factory_reset
|
||||
|
||||
binary_sensor:
|
||||
- platform: zigbee
|
||||
id: zigbee_binary_sensor_1
|
||||
- platform: zigbee
|
||||
id: zigbee_binary_sensor_2
|
||||
lambda: return true;
|
||||
|
||||
zigbee:
|
||||
on_join:
|
||||
- switch.zigbee.publish:
|
||||
id: zigbee_switch_1
|
||||
state: OFF
|
||||
- switch.zigbee.publish:
|
||||
id: zigbee_switch_1
|
||||
state: ON
|
1
tests/components/zigbee/test.nrf52-adafruit.yaml
Normal file
1
tests/components/zigbee/test.nrf52-adafruit.yaml
Normal file
@ -0,0 +1 @@
|
||||
<<: !include common.yaml
|
1
tests/components/zigbee/test.nrf52-mcumgr.yaml
Normal file
1
tests/components/zigbee/test.nrf52-mcumgr.yaml
Normal file
@ -0,0 +1 @@
|
||||
<<: !include common.yaml
|
@ -0,0 +1,20 @@
|
||||
external_components:
|
||||
- source: github://pr#7049
|
||||
components: [logger, nrf52, time, zephyr]
|
||||
refresh: always
|
||||
|
||||
esphome:
|
||||
name: componenttestnrf52
|
||||
friendly_name: $component_name
|
||||
|
||||
nrf52:
|
||||
board: adafruit_itsybitsy_nrf52840
|
||||
|
||||
logger:
|
||||
level: VERY_VERBOSE
|
||||
|
||||
packages:
|
||||
component_under_test: !include
|
||||
file: $component_test_file
|
||||
vars:
|
||||
component_test_file: $component_test_file
|
@ -0,0 +1,20 @@
|
||||
external_components:
|
||||
- source: github://pr#7049
|
||||
components: [logger, nrf52, time, zephyr]
|
||||
refresh: always
|
||||
|
||||
esphome:
|
||||
name: componenttestnrf52
|
||||
friendly_name: $component_name
|
||||
|
||||
nrf52:
|
||||
board: adafruit_feather_nrf52840
|
||||
|
||||
logger:
|
||||
level: VERY_VERBOSE
|
||||
|
||||
packages:
|
||||
component_under_test: !include
|
||||
file: $component_test_file
|
||||
vars:
|
||||
component_test_file: $component_test_file
|
Loading…
x
Reference in New Issue
Block a user