Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Zigbee #2

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion esphome/components/zephyr_mcumgr/ota/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ async def to_code(config):
zephyr_add_prj_conf("MCUMGR_GRP_OS_MCUMGR_PARAMS", True)

zephyr_add_prj_conf("NCS_SAMPLE_MCUMGR_BT_OTA_DFU_SPEEDUP", True)
if config[CONF_HARDWARE_UART]:
if CONF_HARDWARE_UART in config:
cdc_id = UARTS[config[CONF_HARDWARE_UART]][1]
if cdc_id >= 0:
zephyr_add_cdc_acm(config, cdc_id)
Expand Down
31 changes: 31 additions & 0 deletions esphome/components/zephyr_shell/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import esphome.config_validation as cv
from esphome.components.zephyr import zephyr_add_prj_conf, zephyr_add_overlay

CONFIG_SCHEMA = cv.Schema({})


async def to_code(config):
zephyr_add_prj_conf("SHELL", True)
# zephyr_ble_server
zephyr_add_prj_conf("BT_SHELL", True)
# ota
zephyr_add_prj_conf("MCUBOOT_SHELL", True)
# i2c
zephyr_add_prj_conf("I2C_SHELL", True)
# zigbee
zephyr_add_prj_conf("ZIGBEE_SHELL", True)
# select uart for shell
zephyr_add_overlay(
"""
/ {
chosen {
zephyr,shell-uart = &cdc_acm_uart1;
};
};
&zephyr_udc0 {
cdc_acm_uart1: cdc_acm_uart1 {
compatible = "zephyr,cdc-acm-uart";
};
};
"""
)
60 changes: 60 additions & 0 deletions esphome/components/zigbee/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import random
import esphome.config_validation as cv
import esphome.codegen as cg
from esphome.components.zephyr import zephyr_add_prj_conf
from esphome.const import CONF_ID
from esphome.cpp_generator import MockObj
from esphome.core import CORE

zigbee_ns = cg.esphome_ns.namespace("zigbee")
Zigbee = zigbee_ns.class_("Zigbee", cg.Component)

KEY_ZIGBEE = "zigbee"
KEY_EP = "ep"


def zigbee_set_core_data(config):
CORE.data[KEY_ZIGBEE] = {}
CORE.data[KEY_ZIGBEE][KEY_EP] = []
return config


CONFIG_SCHEMA = cv.All(
cv.Schema(
{
cv.GenerateID(CONF_ID): cv.declare_id(Zigbee),
}
).extend(cv.COMPONENT_SCHEMA),
zigbee_set_core_data,
)


async def to_code(config):
cg.add_global(
MockObj(
f"ZBOSS_DECLARE_DEVICE_CTX_EP_VA(zb_device_ctx, &{', &'.join(CORE.data[KEY_ZIGBEE][KEY_EP])})"
)
)
cg.add(MockObj("ZB_AF_REGISTER_DEVICE_CTX(&zb_device_ctx)"))

# 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)
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
162 changes: 162 additions & 0 deletions esphome/components/zigbee/switch/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.components import output, switch
from esphome.const import CONF_OUTPUT
from esphome.cpp_generator import MockObj
from esphome.core import CORE

zigbee_on_off_ns = cg.esphome_ns.namespace("zigbee_on_off")
ZigbeeOnOffSwitch = zigbee_on_off_ns.class_("ZigbeeOnOff", switch.Switch, cg.Component)

cluster_attributes = zigbee_on_off_ns.class_("cluster_attributes_t")

ZB_ZCL_DECLARE_BASIC_ATTRIB_LIST_EXT = cg.global_ns.class_(
"ZB_ZCL_DECLARE_BASIC_ATTRIB_LIST_EXT", cg.Component
)
ZB_ZCL_DECLARE_IDENTIFY_ATTRIB_LIST = cg.global_ns.class_(
"ZB_ZCL_DECLARE_IDENTIFY_ATTRIB_LIST", cg.Component
)
ZB_ZCL_DECLARE_GROUPS_ATTRIB_LIST = cg.global_ns.class_(
"ZB_ZCL_DECLARE_GROUPS_ATTRIB_LIST", cg.Component
)
ZB_ZCL_DECLARE_SCENES_ATTRIB_LIST = cg.global_ns.class_(
"ZB_ZCL_DECLARE_SCENES_ATTRIB_LIST", cg.Component
)
ZB_ZCL_DECLARE_ON_OFF_ATTRIB_LIST = cg.global_ns.class_(
"ZB_ZCL_DECLARE_ON_OFF_ATTRIB_LIST", cg.Component
)
ZB_HA_DECLARE_ON_OFF_OUTPUT_CLUSTER_LIST = cg.global_ns.class_(
"ZB_HA_DECLARE_ON_OFF_OUTPUT_CLUSTER_LIST", cg.Component
)
ZB_EP = cg.global_ns.class_("ZB_EP", cg.Component)

AUTO_LOAD = ["zigbee"]

CONF_CLUSTER_ATTRIBUTES = "cluster_attributes"

CONF_BASIC_ATTRIB_LIST_EXT = "basic_attrib_list_ext"
CONF_IDENTIFY_ATTR_LIST = "identify_attr_list"
CONF_GROUPS_ATTR_LIST = "groups_attr_list"
CONF_SCENES_ATTR_LIST = "scenes_attr_list"
CONF_ON_OFF_ATTR_LIST = "on_off_attr_list"
CONF_ON_OFF_OUTPUT_CLUSTER_LIST = "on_off_output_cluster_list"
CONF_ZB_EP = "zb_ep"

zigbee_ns = cg.esphome_ns.namespace("zigbee")
Zigbee = zigbee_ns.class_("Zigbee", cg.Component)

CONF_ZIGBEE_ID = "zigbee_id"

CONFIG_SCHEMA = (
switch.switch_schema(ZigbeeOnOffSwitch)
.extend(
{
cv.GenerateID(CONF_ZIGBEE_ID): cv.use_id(Zigbee),
cv.Required(CONF_OUTPUT): cv.use_id(output.BinaryOutput),
cv.GenerateID(CONF_CLUSTER_ATTRIBUTES): cv.declare_id(cluster_attributes),
cv.GenerateID(CONF_BASIC_ATTRIB_LIST_EXT): cv.declare_id(
ZB_ZCL_DECLARE_BASIC_ATTRIB_LIST_EXT
),
cv.GenerateID(CONF_IDENTIFY_ATTR_LIST): cv.declare_id(
ZB_ZCL_DECLARE_IDENTIFY_ATTRIB_LIST
),
cv.GenerateID(CONF_GROUPS_ATTR_LIST): cv.declare_id(
ZB_ZCL_DECLARE_GROUPS_ATTRIB_LIST
),
cv.GenerateID(CONF_SCENES_ATTR_LIST): cv.declare_id(
ZB_ZCL_DECLARE_SCENES_ATTRIB_LIST
),
cv.GenerateID(CONF_ON_OFF_ATTR_LIST): cv.declare_id(
ZB_ZCL_DECLARE_ON_OFF_ATTRIB_LIST
),
cv.GenerateID(CONF_ON_OFF_OUTPUT_CLUSTER_LIST): cv.declare_id(
ZB_HA_DECLARE_ON_OFF_OUTPUT_CLUSTER_LIST
),
cv.GenerateID(CONF_ZB_EP): cv.declare_id(ZB_EP),
}
)
.extend(cv.COMPONENT_SCHEMA)
)

KEY_ZIGBEE = "zigbee"
KEY_EP = "ep"


async def to_code(config):
cg.add_global(MockObj(f"{cluster_attributes} {config[CONF_CLUSTER_ATTRIBUTES]}"))

# Declare attributes

# basic cluster attributes data
cg.add_global(
MockObj(
f"ZB_ZCL_DECLARE_BASIC_ATTRIB_LIST_EXT({config[CONF_BASIC_ATTRIB_LIST_EXT]}, &{config[CONF_CLUSTER_ATTRIBUTES]}.basic_attr.zcl_version, &{config[CONF_CLUSTER_ATTRIBUTES]}.basic_attr.app_version, &{config[CONF_CLUSTER_ATTRIBUTES]}.basic_attr.stack_version, &{config[CONF_CLUSTER_ATTRIBUTES]}.basic_attr.hw_version, {config[CONF_CLUSTER_ATTRIBUTES]}.basic_attr.mf_name, {config[CONF_CLUSTER_ATTRIBUTES]}.basic_attr.model_id, {config[CONF_CLUSTER_ATTRIBUTES]}.basic_attr.date_code, &{config[CONF_CLUSTER_ATTRIBUTES]}.basic_attr.power_source, {config[CONF_CLUSTER_ATTRIBUTES]}.basic_attr.location_id, &{config[CONF_CLUSTER_ATTRIBUTES]}.basic_attr.ph_env, {config[CONF_CLUSTER_ATTRIBUTES]}.basic_attr.sw_ver)"
)
)

# identify cluster attributes data
cg.add_global(
MockObj(
f"ZB_ZCL_DECLARE_IDENTIFY_ATTRIB_LIST({config[CONF_IDENTIFY_ATTR_LIST]}, &{config[CONF_CLUSTER_ATTRIBUTES]}.identify_attr.identify_time)"
)
)

# groups cluster attributes data
cg.add_global(
MockObj(
f"ZB_ZCL_DECLARE_GROUPS_ATTRIB_LIST({config[CONF_GROUPS_ATTR_LIST]}, &{config[CONF_CLUSTER_ATTRIBUTES]}.groups_attr.name_support)"
)
)

# scenes cluster attribute data
cg.add_global(
MockObj(
f"ZB_ZCL_DECLARE_SCENES_ATTRIB_LIST({config[CONF_SCENES_ATTR_LIST]}, &{config[CONF_CLUSTER_ATTRIBUTES]}.scenes_attr.scene_count, &{config[CONF_CLUSTER_ATTRIBUTES]}.scenes_attr.current_scene, &{config[CONF_CLUSTER_ATTRIBUTES]}.scenes_attr.current_group, &{config[CONF_CLUSTER_ATTRIBUTES]}.scenes_attr.scene_valid, &{config[CONF_CLUSTER_ATTRIBUTES]}.scenes_attr.name_support)"
)
)

# on/off cluster attributes data
cg.add_global(
MockObj(
f"ZB_ZCL_DECLARE_ON_OFF_ATTRIB_LIST({config[CONF_ON_OFF_ATTR_LIST]}, &{config[CONF_CLUSTER_ATTRIBUTES]}.on_off_attr.on_off)"
)
)

# Declare device
cg.add_global(
MockObj(
f"ZB_HA_DECLARE_ON_OFF_OUTPUT_CLUSTER_LIST({config[CONF_ON_OFF_OUTPUT_CLUSTER_LIST]}, {config[CONF_ON_OFF_ATTR_LIST]}, {config[CONF_BASIC_ATTRIB_LIST_EXT]}, {config[CONF_IDENTIFY_ATTR_LIST]}, {config[CONF_GROUPS_ATTR_LIST]}, {config[CONF_SCENES_ATTR_LIST]})"
)
)

zb_ep = config[CONF_ZB_EP]
end_point = int(str(zb_ep)[str(zb_ep).find("_id") + 4 :] or "1") + 9

ep_macro_name = ""
if str(config[CONF_CLUSTER_ATTRIBUTES])[-1].isdigit():
ep_macro_name = "ESPHOME_"
ep_macro_name += "ZB_HA_DECLARE_ON_OFF_OUTPUT_EP"

cg.add_global(
MockObj(
f"{ep_macro_name}({config[CONF_ZB_EP]}, {end_point}, {config[CONF_ON_OFF_OUTPUT_CLUSTER_LIST]})"
)
)

CORE.data[KEY_ZIGBEE][KEY_EP] += [str(config[CONF_ZB_EP])]

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_cluster_attributes(MockObj(f"&{config[CONF_CLUSTER_ATTRIBUTES]}")))
hub = await cg.get_variable(config[CONF_ZIGBEE_ID])
cg.add(
getattr(hub, "add_callback")(
end_point,
MockObj(
f"std::bind(&zigbee_on_off::ZigbeeOnOff::zcl_device_cb, {var}, std::placeholders::_1)"
),
)
)
112 changes: 112 additions & 0 deletions esphome/components/zigbee/switch/zigbee_on_off.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#include "zigbee_on_off.h"
#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_on_off {

#define HA_ESP_LIGHT_ENDPOINT 14

Check failure on line 16 in esphome/components/zigbee/switch/zigbee_on_off.cpp

View workflow job for this annotation

GitHub Actions / Run script/ci-custom

#define macros for integer constants are not allowed, please use static const uint8_t HA_ESP_LIGHT_ENDPOINT = 14; style instead (replace uint8_t with the appropriate datatype). See also Google style guide.
#define HA_ESP_LIGHT_ENDPOINT2 15

Check failure on line 17 in esphome/components/zigbee/switch/zigbee_on_off.cpp

View workflow job for this annotation

GitHub Actions / Run script/ci-custom

#define macros for integer constants are not allowed, please use static const uint8_t HA_ESP_LIGHT_ENDPOINT2 = 15; style instead (replace uint8_t with the appropriate datatype). See also Google style guide.
#define BULB_INIT_BASIC_LOCATION_DESC "TODO"
#define BULB_INIT_BASIC_PH_ENV ZB_ZCL_BASIC_ENV_UNSPECIFIED
#define BULB_INIT_BASIC_MODEL_ID "TODO"
#define BULB_INIT_BASIC_DATE_CODE "20200329"
#define BULB_INIT_BASIC_APP_VERSION 00

Check failure on line 22 in esphome/components/zigbee/switch/zigbee_on_off.cpp

View workflow job for this annotation

GitHub Actions / Run script/ci-custom

#define macros for integer constants are not allowed, please use static const uint8_t BULB_INIT_BASIC_APP_VERSION = 00; style instead (replace uint8_t with the appropriate datatype). See also Google style guide.
#define BULB_INIT_BASIC_HW_VERSION 00

Check failure on line 23 in esphome/components/zigbee/switch/zigbee_on_off.cpp

View workflow job for this annotation

GitHub Actions / Run script/ci-custom

#define macros for integer constants are not allowed, please use static const uint8_t BULB_INIT_BASIC_HW_VERSION = 00; style instead (replace uint8_t with the appropriate datatype). See also Google style guide.
#define BULB_INIT_BASIC_STACK_VERSION 00

Check failure on line 24 in esphome/components/zigbee/switch/zigbee_on_off.cpp

View workflow job for this annotation

GitHub Actions / Run script/ci-custom

#define macros for integer constants are not allowed, please use static const uint8_t BULB_INIT_BASIC_STACK_VERSION = 00; style instead (replace uint8_t with the appropriate datatype). See also Google style guide.
#define BULB_INIT_BASIC_MANUF_NAME "esphome"

static const char *const TAG = "zigbee_on_off.switch";

void ZigbeeOnOff::dump_config() { LOG_SWITCH("", "Zigbee Switch", this); }

void ZigbeeOnOff::setup() {
cluster_attributes_->basic_attr.zcl_version = ZB_ZCL_VERSION;
cluster_attributes_->basic_attr.app_version = BULB_INIT_BASIC_APP_VERSION;
cluster_attributes_->basic_attr.stack_version = BULB_INIT_BASIC_STACK_VERSION;
cluster_attributes_->basic_attr.hw_version = BULB_INIT_BASIC_HW_VERSION;

ZB_ZCL_SET_STRING_VAL(cluster_attributes_->basic_attr.mf_name, BULB_INIT_BASIC_MANUF_NAME,
ZB_ZCL_STRING_CONST_SIZE(BULB_INIT_BASIC_MANUF_NAME));

ZB_ZCL_SET_STRING_VAL(cluster_attributes_->basic_attr.model_id, BULB_INIT_BASIC_MODEL_ID,
ZB_ZCL_STRING_CONST_SIZE(BULB_INIT_BASIC_MODEL_ID));

ZB_ZCL_SET_STRING_VAL(cluster_attributes_->basic_attr.date_code, BULB_INIT_BASIC_DATE_CODE,
ZB_ZCL_STRING_CONST_SIZE(BULB_INIT_BASIC_DATE_CODE));

cluster_attributes_->basic_attr.power_source = ZB_ZCL_BASIC_POWER_SOURCE_DC_SOURCE;

ZB_ZCL_SET_STRING_VAL(cluster_attributes_->basic_attr.location_id, BULB_INIT_BASIC_LOCATION_DESC,
ZB_ZCL_STRING_CONST_SIZE(BULB_INIT_BASIC_LOCATION_DESC));

cluster_attributes_->basic_attr.ph_env = BULB_INIT_BASIC_PH_ENV;

/* identify cluster attributes data */
cluster_attributes_->identify_attr.identify_time = ZB_ZCL_IDENTIFY_IDENTIFY_TIME_DEFAULT_VALUE;
/* groups cluster attributes data */
cluster_attributes_->groups_attr.name_support = 0;

bool initial_state = this->get_initial_state_with_restore_mode().value_or(false);

if (initial_state) {
/* on/off cluster attributes data */
cluster_attributes_->on_off_attr.on_off = ZB_ZCL_ON_OFF_IS_ON;
this->turn_on();
} else {
/* on/off cluster attributes data */
cluster_attributes_->on_off_attr.on_off = ZB_ZCL_ON_OFF_IS_OFF;
this->turn_off();
}
}

void ZigbeeOnOff::write_state(bool state) {
if (state) {
this->output_->turn_on();
} else {
this->output_->turn_off();
}
this->publish_state(state);
}

void ZigbeeOnOff::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_ON_OFF) {
uint8_t value = p_device_cb_param->cb_param.set_attr_value_param.values.data8;
ESP_LOGI(TAG, "on/off attribute setting to %hd", value);

if (attr_id == ZB_ZCL_ATTR_ON_OFF_ON_OFF_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);
}

} // namespace zigbee_on_off
} // namespace esphome
Loading
Loading