mirror of
https://github.com/esphome/esphome.git
synced 2024-12-11 14:49:20 +01:00
zigbee binary_sensor and switch
This commit is contained in:
parent
ab620acd4f
commit
bdf2d21637
263
esphome/components/zigbee/__init__.py
Normal file
263
esphome/components/zigbee/__init__.py
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
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, KEY_ZIGBEE, zigbee_set_core_data
|
||||||
|
import esphome.config_validation as cv
|
||||||
|
from esphome.const import CONF_BINARY_SENSOR, CONF_ID, CONF_PLATFORM, __version__
|
||||||
|
from esphome.core import CORE, ID, coroutine_with_priority
|
||||||
|
from esphome.cpp_generator import (
|
||||||
|
AssignmentExpression,
|
||||||
|
MockObj,
|
||||||
|
VariableDeclarationExpression,
|
||||||
|
)
|
||||||
|
import esphome.final_validate as fv
|
||||||
|
|
||||||
|
from .const import (
|
||||||
|
CONF_BASIC_ATTRIB_LIST_EXT,
|
||||||
|
CONF_BASIC_ATTRS_EXT,
|
||||||
|
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_SWITCH,
|
||||||
|
CONF_ZIGBEE_ID,
|
||||||
|
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
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
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 count_ep_by_type(fconf, type):
|
||||||
|
count = 0
|
||||||
|
if type in fconf:
|
||||||
|
for entity in fconf[type]:
|
||||||
|
if CONF_PLATFORM in entity and entity[CONF_PLATFORM] == KEY_ZIGBEE:
|
||||||
|
count += 1
|
||||||
|
return count
|
||||||
|
|
||||||
|
|
||||||
|
def validate_number_of_ep(config):
|
||||||
|
count = 0
|
||||||
|
fconf = fv.full_config.get()
|
||||||
|
count += count_ep_by_type(fconf, CONF_SWITCH)
|
||||||
|
count += count_ep_by_type(fconf, CONF_BINARY_SENSOR)
|
||||||
|
if count > 8:
|
||||||
|
raise cv.Invalid(f"Maximum number of EP is {CONF_MAX_EP_NUMBER}")
|
||||||
|
|
||||||
|
|
||||||
|
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):
|
||||||
|
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_
|
||||||
|
|
||||||
|
|
||||||
|
def zigbee_new_cluster_list(id_: ID, *args):
|
||||||
|
assert isinstance(id_, ID)
|
||||||
|
list = []
|
||||||
|
for arg in args:
|
||||||
|
list.append(f"{arg}")
|
||||||
|
obj = cg.RawExpression(f'{id_.type}({id_}, {", ".join(list)})')
|
||||||
|
CORE.add_global(obj)
|
||||||
|
return id_
|
||||||
|
|
||||||
|
|
||||||
|
def zigbee_register_ep(id_: ID, cluster):
|
||||||
|
assert isinstance(id_, ID)
|
||||||
|
ep = len(CORE.data[KEY_ZIGBEE][KEY_EP]) + 1
|
||||||
|
CORE.data[KEY_ZIGBEE][KEY_EP] += [str(id_)]
|
||||||
|
obj = cg.RawExpression(f"{id_.type}({id_}, {ep}, {cluster})")
|
||||||
|
CORE.add_global(obj)
|
||||||
|
return ep
|
115
esphome/components/zigbee/binary_sensor/__init__.py
Normal file
115
esphome/components/zigbee/binary_sensor/__init__.py
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
from esphome import automation
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import binary_sensor
|
||||||
|
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,
|
||||||
|
zigbee_assign,
|
||||||
|
zigbee_new_attr_list,
|
||||||
|
zigbee_new_cluster_list,
|
||||||
|
zigbee_new_variable,
|
||||||
|
zigbee_register_ep,
|
||||||
|
zigbee_set_string,
|
||||||
|
)
|
||||||
|
from ..const import (
|
||||||
|
CONF_BASIC_ATTRIB_LIST_EXT,
|
||||||
|
CONF_BINARY_ATTRS,
|
||||||
|
CONF_BINARY_INPUT_ATTRIB_LIST,
|
||||||
|
CONF_BINARY_INPUT_CLUSTER_LIST,
|
||||||
|
CONF_BINARY_INPUT_EP,
|
||||||
|
CONF_GROUPS_ATTRIB_LIST,
|
||||||
|
CONF_IDENTIFY_ATTRIB_LIST,
|
||||||
|
CONF_SCENES_ATTRIB_LIST,
|
||||||
|
CONF_ZIGBEE_ID,
|
||||||
|
BinaryAttrs,
|
||||||
|
esphome_zb_ha_declare_binary_input_ep,
|
||||||
|
zigbee_ns,
|
||||||
|
)
|
||||||
|
|
||||||
|
AUTO_LOAD = ["zigbee"]
|
||||||
|
|
||||||
|
ZigbeeBinarySensor = zigbee_ns.class_(
|
||||||
|
"ZigbeeBinarySensor", binary_sensor.BinarySensor, cg.Component
|
||||||
|
)
|
||||||
|
CONFIG_SCHEMA = (
|
||||||
|
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.GenerateID(CONF_BINARY_INPUT_CLUSTER_LIST): cv.declare_id(
|
||||||
|
cg.global_ns.namespace(
|
||||||
|
"ESPHOME_ZB_HA_DECLARE_BINARY_INPUT_CLUSTER_LIST"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
cv.GenerateID(CONF_BINARY_INPUT_EP): cv.declare_id(
|
||||||
|
esphome_zb_ha_declare_binary_input_ep
|
||||||
|
),
|
||||||
|
cv.Optional(CONF_LAMBDA): cv.returning_lambda,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
.extend(ZigbeeBaseSchema)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@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 = zigbee_new_cluster_list(
|
||||||
|
config[CONF_BINARY_INPUT_CLUSTER_LIST],
|
||||||
|
attr_list,
|
||||||
|
config[CONF_BASIC_ATTRIB_LIST_EXT],
|
||||||
|
config[CONF_IDENTIFY_ATTRIB_LIST],
|
||||||
|
config[CONF_GROUPS_ATTRIB_LIST],
|
||||||
|
config[CONF_SCENES_ATTRIB_LIST],
|
||||||
|
)
|
||||||
|
|
||||||
|
ep = zigbee_register_ep(config[CONF_BINARY_INPUT_EP], cluster)
|
||||||
|
|
||||||
|
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(ep))
|
||||||
|
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,58 @@
|
|||||||
|
#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_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ZigbeeBinarySensor::set_parent(Zigbee *parent) { this->parent_ = parent; }
|
||||||
|
|
||||||
|
} // namespace zigbee
|
||||||
|
} // namespace esphome
|
||||||
|
#endif
|
@ -0,0 +1,96 @@
|
|||||||
|
#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 ESPHOME_ZB_HA_DECLARE_BINARY_INPUT_CLUSTER_LIST(cluster_list_name, binary_attr_list, basic_attr_list, \
|
||||||
|
identify_attr_list, groups_attr_list, scenes_attr_list) \
|
||||||
|
zb_zcl_cluster_desc_t cluster_list_name[] = { \
|
||||||
|
ZB_ZCL_CLUSTER_DESC(ZB_ZCL_CLUSTER_ID_IDENTIFY, ZB_ZCL_ARRAY_SIZE(identify_attr_list, zb_zcl_attr_t), \
|
||||||
|
(identify_attr_list), ZB_ZCL_CLUSTER_SERVER_ROLE, ZB_ZCL_MANUF_CODE_INVALID), \
|
||||||
|
ZB_ZCL_CLUSTER_DESC(ZB_ZCL_CLUSTER_ID_BASIC, ZB_ZCL_ARRAY_SIZE(basic_attr_list, zb_zcl_attr_t), \
|
||||||
|
(basic_attr_list), ZB_ZCL_CLUSTER_SERVER_ROLE, ZB_ZCL_MANUF_CODE_INVALID), \
|
||||||
|
ZB_ZCL_CLUSTER_DESC(ZB_ZCL_CLUSTER_ID_BINARY_INPUT, ZB_ZCL_ARRAY_SIZE(binary_attr_list, zb_zcl_attr_t), \
|
||||||
|
(binary_attr_list), ZB_ZCL_CLUSTER_SERVER_ROLE, ZB_ZCL_MANUF_CODE_INVALID), \
|
||||||
|
ZB_ZCL_CLUSTER_DESC(ZB_ZCL_CLUSTER_ID_GROUPS, ZB_ZCL_ARRAY_SIZE(groups_attr_list, zb_zcl_attr_t), \
|
||||||
|
(groups_attr_list), ZB_ZCL_CLUSTER_SERVER_ROLE, ZB_ZCL_MANUF_CODE_INVALID), \
|
||||||
|
ZB_ZCL_CLUSTER_DESC(ZB_ZCL_CLUSTER_ID_SCENES, ZB_ZCL_ARRAY_SIZE(scenes_attr_list, zb_zcl_attr_t), \
|
||||||
|
(scenes_attr_list), ZB_ZCL_CLUSTER_SERVER_ROLE, ZB_ZCL_MANUF_CODE_INVALID)}
|
||||||
|
|
||||||
|
#define ESPHOME_ZB_HA_DEVICE_VER_SIMPLE_SENSOR 0
|
||||||
|
|
||||||
|
#define ESPHOME_ZB_ZCL_DECLARE_BINARY_INPUT_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, \
|
||||||
|
ESPHOME_ZB_HA_DEVICE_VER_SIMPLE_SENSOR, \
|
||||||
|
0, \
|
||||||
|
in_clust_num, \
|
||||||
|
out_clust_num, \
|
||||||
|
{ZB_ZCL_CLUSTER_ID_BASIC, ZB_ZCL_CLUSTER_ID_IDENTIFY, ZB_ZCL_CLUSTER_ID_BINARY_INPUT, \
|
||||||
|
ZB_ZCL_CLUSTER_ID_SCENES, ZB_ZCL_CLUSTER_ID_GROUPS}}
|
||||||
|
|
||||||
|
#define ESPHOME_ZB_HA_BINARY_INPUT_REPORT_ATTR_COUNT ZB_ZCL_BINARY_INPUT_REPORT_ATTR_COUNT
|
||||||
|
#define ESPHOME_ZB_HA_BINARY_INPUT_IN_CLUSTER_NUM 5 // server roles in ESPHOME_ZB_HA_DECLARE_BINARY_INPUT_CLUSTER_LIST
|
||||||
|
#define ESPHOME_ZB_HA_BINARY_INPUT_OUT_CLUSTER_NUM 0 // client roles in ESPHOME_ZB_HA_DECLARE_BINARY_INPUT_CLUSTER_LIST
|
||||||
|
|
||||||
|
#define ESPHOME_ZB_HA_DECLARE_BINARY_INPUT_EP(ep_name, ep_id, cluster_list) \
|
||||||
|
ESPHOME_ZB_ZCL_DECLARE_BINARY_INPUT_SIMPLE_DESC(ep_name, ep_id, ESPHOME_ZB_HA_BINARY_INPUT_IN_CLUSTER_NUM, \
|
||||||
|
ESPHOME_ZB_HA_BINARY_INPUT_OUT_CLUSTER_NUM); \
|
||||||
|
ZBOSS_DEVICE_DECLARE_REPORTING_CTX(reporting_info##ep_name, ESPHOME_ZB_HA_BINARY_INPUT_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, \
|
||||||
|
ESPHOME_ZB_HA_BINARY_INPUT_REPORT_ATTR_COUNT, reporting_info##ep_name, 0, NULL)
|
||||||
|
|
||||||
|
// it cannot have ESPHOME prefix since it is used outside
|
||||||
|
#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 Component, public binary_sensor::BinarySensor {
|
||||||
|
public:
|
||||||
|
void set_template(std::function<optional<bool>()> &&f) { this->f_ = f; }
|
||||||
|
|
||||||
|
void setup() override;
|
||||||
|
void loop() override;
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
void set_parent(Zigbee *parent);
|
||||||
|
void set_ep(zb_uint8_t ep) { this->ep_ = ep; }
|
||||||
|
void set_cluster_attributes(BinaryAttrs &cluster_attributes) { this->cluster_attributes_ = &cluster_attributes; }
|
||||||
|
|
||||||
|
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::function<optional<bool>()> f_{nullptr};
|
||||||
|
zb_uint8_t ep_{0};
|
||||||
|
Zigbee *parent_{nullptr};
|
||||||
|
BinaryAttrs *cluster_attributes_{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace zigbee
|
||||||
|
} // namespace esphome
|
||||||
|
#endif
|
61
esphome/components/zigbee/const.py
Normal file
61
esphome/components/zigbee/const.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
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_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"
|
||||||
|
CONF_BINARY_INPUT_CLUSTER_LIST = "binary_input_cluster_list"
|
||||||
|
CONF_BINARY_INPUT_EP = "binary_input_ep"
|
||||||
|
esphome_zb_ha_declare_binary_input_ep = cg.global_ns.namespace(
|
||||||
|
"ESPHOME_ZB_HA_DECLARE_BINARY_INPUT_EP"
|
||||||
|
)
|
||||||
|
|
||||||
|
# output
|
||||||
|
CONF_BINARY_OUTPUT_ATTRIB_LIST = "binary_output_attrib_list"
|
||||||
|
CONF_BINARY_OUTPUT_CLUSTER_LIST = "binary_output_cluster_list"
|
||||||
|
|
||||||
|
CONF_BINARY_OUTPUT_EP = "binary_output_ep"
|
||||||
|
esphome_zb_ha_declare_binary_output_ep = cg.global_ns.namespace(
|
||||||
|
"ESPHOME_ZB_HA_DECLARE_BINARY_OUTPUT_EP"
|
||||||
|
)
|
110
esphome/components/zigbee/switch/__init__.py
Normal file
110
esphome/components/zigbee/switch/__init__.py
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
from esphome import automation
|
||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.components import output, switch
|
||||||
|
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,
|
||||||
|
zigbee_assign,
|
||||||
|
zigbee_new_attr_list,
|
||||||
|
zigbee_new_cluster_list,
|
||||||
|
zigbee_new_variable,
|
||||||
|
zigbee_register_ep,
|
||||||
|
zigbee_set_string,
|
||||||
|
)
|
||||||
|
from ..const import (
|
||||||
|
CONF_BASIC_ATTRIB_LIST_EXT,
|
||||||
|
CONF_BINARY_ATTRS,
|
||||||
|
CONF_BINARY_OUTPUT_ATTRIB_LIST,
|
||||||
|
CONF_BINARY_OUTPUT_CLUSTER_LIST,
|
||||||
|
CONF_BINARY_OUTPUT_EP,
|
||||||
|
CONF_GROUPS_ATTRIB_LIST,
|
||||||
|
CONF_IDENTIFY_ATTRIB_LIST,
|
||||||
|
CONF_SCENES_ATTRIB_LIST,
|
||||||
|
CONF_ZIGBEE_ID,
|
||||||
|
BinaryAttrs,
|
||||||
|
esphome_zb_ha_declare_binary_output_ep,
|
||||||
|
zigbee_ns,
|
||||||
|
)
|
||||||
|
|
||||||
|
AUTO_LOAD = ["zigbee"]
|
||||||
|
|
||||||
|
ZigbeeSwitch = zigbee_ns.class_("ZigbeeSwitch", switch.Switch, cg.Component)
|
||||||
|
|
||||||
|
CONFIG_SCHEMA = (
|
||||||
|
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"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
cv.GenerateID(CONF_BINARY_OUTPUT_CLUSTER_LIST): cv.declare_id(
|
||||||
|
cg.global_ns.namespace(
|
||||||
|
"ESPHOME_ZB_HA_DECLARE_BINARY_OUTPUT_CLUSTER_LIST"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
cv.GenerateID(CONF_BINARY_OUTPUT_EP): cv.declare_id(
|
||||||
|
esphome_zb_ha_declare_binary_output_ep
|
||||||
|
),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.extend(cv.COMPONENT_SCHEMA)
|
||||||
|
.extend(ZigbeeBaseSchema)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@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 = zigbee_new_cluster_list(
|
||||||
|
config[CONF_BINARY_OUTPUT_CLUSTER_LIST],
|
||||||
|
attr_list,
|
||||||
|
config[CONF_BASIC_ATTRIB_LIST_EXT],
|
||||||
|
config[CONF_IDENTIFY_ATTRIB_LIST],
|
||||||
|
config[CONF_GROUPS_ATTRIB_LIST],
|
||||||
|
config[CONF_SCENES_ATTRIB_LIST],
|
||||||
|
)
|
||||||
|
|
||||||
|
ep = zigbee_register_ep(config[CONF_BINARY_OUTPUT_EP], cluster)
|
||||||
|
|
||||||
|
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(ep))
|
||||||
|
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
|
93
esphome/components/zigbee/switch/zigbee_switch.cpp
Normal file
93
esphome/components/zigbee/switch/zigbee_switch.cpp
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#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() {
|
||||||
|
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) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ZigbeeSwitch::set_parent(Zigbee *parent) {
|
||||||
|
this->parent_ = parent;
|
||||||
|
this->parent_->add_callback(this->ep_, [this](zb_bufid_t bufid) { this->zcl_device_cb_(bufid); });
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace zigbee
|
||||||
|
} // namespace esphome
|
||||||
|
#endif
|
135
esphome/components/zigbee/switch/zigbee_switch.h
Normal file
135
esphome/components/zigbee/switch/zigbee_switch.h
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
#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 ESPHOME_ZB_HA_DEVICE_VER_SIMPLE_OUTPUT 0 // TODO what to set here?
|
||||||
|
#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_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, \
|
||||||
|
ESPHOME_ZB_HA_DEVICE_VER_SIMPLE_OUTPUT, \
|
||||||
|
0, \
|
||||||
|
in_clust_num, \
|
||||||
|
out_clust_num, \
|
||||||
|
{ZB_ZCL_CLUSTER_ID_BASIC, ZB_ZCL_CLUSTER_ID_IDENTIFY, ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT, \
|
||||||
|
ZB_ZCL_CLUSTER_ID_SCENES, ZB_ZCL_CLUSTER_ID_GROUPS}}
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
#define ESPHOME_ZB_HA_BINARY_OUTPUT_REPORT_ATTR_COUNT 2
|
||||||
|
#define ESPHOME_ZB_HA_BINARY_OUTPUT_IN_CLUSTER_NUM \
|
||||||
|
5 // server roles in ESPHOME_ZB_HA_DECLARE_BINARY_OUTPUT_CLUSTER_LIST
|
||||||
|
#define ESPHOME_ZB_HA_BINARY_OUTPUT_OUT_CLUSTER_NUM \
|
||||||
|
0 // client roles in ESPHOME_ZB_HA_DECLARE_BINARY_OUTPUT_CLUSTER_LIST
|
||||||
|
|
||||||
|
#define ESPHOME_ZB_HA_DECLARE_BINARY_OUTPUT_EP(ep_name, ep_id, cluster_list) \
|
||||||
|
ESPHOME_ZB_ZCL_DECLARE_BINARY_OUTPUT_SIMPLE_DESC(ep_name, ep_id, ESPHOME_ZB_HA_BINARY_OUTPUT_IN_CLUSTER_NUM, \
|
||||||
|
ESPHOME_ZB_HA_BINARY_OUTPUT_OUT_CLUSTER_NUM); \
|
||||||
|
ZBOSS_DEVICE_DECLARE_REPORTING_CTX(reporting_info##ep_name, ESPHOME_ZB_HA_BINARY_OUTPUT_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, \
|
||||||
|
ESPHOME_ZB_HA_BINARY_OUTPUT_REPORT_ATTR_COUNT, reporting_info##ep_name, 0, NULL)
|
||||||
|
|
||||||
|
#define ESPHOME_ZB_HA_DECLARE_BINARY_OUTPUT_CLUSTER_LIST(cluster_list_name, binary_attr_list, basic_attr_list, \
|
||||||
|
identify_attr_list, groups_attr_list, scenes_attr_list) \
|
||||||
|
zb_zcl_cluster_desc_t cluster_list_name[] = { \
|
||||||
|
ZB_ZCL_CLUSTER_DESC(ZB_ZCL_CLUSTER_ID_IDENTIFY, ZB_ZCL_ARRAY_SIZE(identify_attr_list, zb_zcl_attr_t), \
|
||||||
|
(identify_attr_list), ZB_ZCL_CLUSTER_SERVER_ROLE, ZB_ZCL_MANUF_CODE_INVALID), \
|
||||||
|
ZB_ZCL_CLUSTER_DESC(ZB_ZCL_CLUSTER_ID_BASIC, ZB_ZCL_ARRAY_SIZE(basic_attr_list, zb_zcl_attr_t), \
|
||||||
|
(basic_attr_list), ZB_ZCL_CLUSTER_SERVER_ROLE, ZB_ZCL_MANUF_CODE_INVALID), \
|
||||||
|
ZB_ZCL_CLUSTER_DESC(ZB_ZCL_CLUSTER_ID_BINARY_OUTPUT, ZB_ZCL_ARRAY_SIZE(binary_attr_list, zb_zcl_attr_t), \
|
||||||
|
(binary_attr_list), ZB_ZCL_CLUSTER_SERVER_ROLE, ZB_ZCL_MANUF_CODE_INVALID), \
|
||||||
|
ZB_ZCL_CLUSTER_DESC(ZB_ZCL_CLUSTER_ID_GROUPS, ZB_ZCL_ARRAY_SIZE(groups_attr_list, zb_zcl_attr_t), \
|
||||||
|
(groups_attr_list), ZB_ZCL_CLUSTER_SERVER_ROLE, ZB_ZCL_MANUF_CODE_INVALID), \
|
||||||
|
ZB_ZCL_CLUSTER_DESC(ZB_ZCL_CLUSTER_ID_SCENES, ZB_ZCL_ARRAY_SIZE(scenes_attr_list, zb_zcl_attr_t), \
|
||||||
|
(scenes_attr_list), ZB_ZCL_CLUSTER_SERVER_ROLE, ZB_ZCL_MANUF_CODE_INVALID)}
|
||||||
|
|
||||||
|
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 Component {
|
||||||
|
public:
|
||||||
|
void set_output(output::BinaryOutput *output) { this->output_ = output; }
|
||||||
|
void set_cluster_attributes(BinaryAttrs &cluster_attributes) { this->cluster_attributes_ = &cluster_attributes; }
|
||||||
|
void set_ep(zb_uint8_t ep) { this->ep_ = ep; }
|
||||||
|
|
||||||
|
void setup() override;
|
||||||
|
float get_setup_priority() const override { return setup_priority::HARDWARE - 1.0f; }
|
||||||
|
void dump_config() override;
|
||||||
|
|
||||||
|
void set_parent(Zigbee *parent);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void write_state(bool state) override;
|
||||||
|
void zcl_device_cb_(zb_bufid_t bufid);
|
||||||
|
|
||||||
|
output::BinaryOutput *output_;
|
||||||
|
BinaryAttrs *cluster_attributes_ = nullptr;
|
||||||
|
zb_uint8_t ep_{0};
|
||||||
|
Zigbee *parent_{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace zigbee
|
||||||
|
} // namespace esphome
|
||||||
|
#endif
|
161
esphome/components/zigbee/zigbee_component.cpp
Normal file
161
esphome/components/zigbee/zigbee_component.cpp
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
#include "zigbee_component.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";
|
||||||
|
|
||||||
|
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) {
|
||||||
|
// this is from wrong thread
|
||||||
|
this->join_trigger_->trigger();
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
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') {
|
||||||
|
// called from wrong thread
|
||||||
|
this->join_trigger_->trigger();
|
||||||
|
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::setup() {
|
||||||
|
global_zigbee = this;
|
||||||
|
auto err = settings_subsys_init();
|
||||||
|
if (err) {
|
||||||
|
ESP_LOGE(TAG, "Failed to initialize settings subsystem, err: %d", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
65
esphome/components/zigbee/zigbee_component.h
Normal file
65
esphome/components/zigbee/zigbee_component.h
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#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>
|
||||||
|
}
|
||||||
|
|
||||||
|
#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)
|
||||||
|
|
||||||
|
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) { callbacks_[endpoint] = 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;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static void zcl_device_cb(zb_bufid_t bufid);
|
||||||
|
std::map<zb_uint8_t, std::function<void(zb_bufid_t bufid)>> callbacks_;
|
||||||
|
Trigger<> *join_trigger_{new Trigger<>()};
|
||||||
|
bool need_flush_{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
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(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace zigbee
|
||||||
|
} // namespace esphome
|
||||||
|
#endif
|
22
esphome/components/zigbee_ctx/__init__.py
Normal file
22
esphome/components/zigbee_ctx/__init__.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import esphome.codegen as cg
|
||||||
|
from esphome.core import CORE, coroutine_with_priority
|
||||||
|
|
||||||
|
KEY_ZIGBEE = "zigbee"
|
||||||
|
KEY_EP = "ep"
|
||||||
|
|
||||||
|
|
||||||
|
def zigbee_set_core_data(config):
|
||||||
|
CORE.data[KEY_ZIGBEE] = {}
|
||||||
|
CORE.data[KEY_ZIGBEE][KEY_EP] = []
|
||||||
|
return config
|
||||||
|
|
||||||
|
|
||||||
|
@coroutine_with_priority(10.0)
|
||||||
|
async def to_code(config):
|
||||||
|
if len(CORE.data[KEY_ZIGBEE][KEY_EP]) > 0:
|
||||||
|
cg.add_global(
|
||||||
|
cg.RawExpression(
|
||||||
|
f"ZBOSS_DECLARE_DEVICE_CTX_EP_VA(zb_device_ctx, &{', &'.join(CORE.data[KEY_ZIGBEE][KEY_EP])})"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
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
|
Loading…
Reference in New Issue
Block a user