From f5e80e7482912c80cfd3d02f669dcb09e1e2931a Mon Sep 17 00:00:00 2001 From: Rohit Jadhav Date: Thu, 23 Mar 2023 16:40:25 +0530 Subject: [PATCH] Added support of mode select --- components/esp_matter/CMakeLists.txt | 3 + .../esp_matter/esp_matter_attribute.cpp | 36 ++++++++ components/esp_matter/esp_matter_attribute.h | 11 +++ .../esp_matter/esp_matter_attribute_utils.cpp | 92 ++++++++++++++++++- .../esp_matter/esp_matter_attribute_utils.h | 13 ++- components/esp_matter/esp_matter_cluster.cpp | 48 ++++++++++ components/esp_matter/esp_matter_cluster.h | 13 +++ components/esp_matter/esp_matter_command.cpp | 21 +++++ components/esp_matter/esp_matter_command.h | 6 ++ components/esp_matter/esp_matter_core.cpp | 15 +++ components/esp_matter/esp_matter_core.h | 2 + components/esp_matter/esp_matter_endpoint.cpp | 32 +++++++ components/esp_matter/esp_matter_endpoint.h | 13 +++ components/esp_matter/esp_matter_feature.cpp | 29 ++++++ components/esp_matter/esp_matter_feature.h | 18 ++++ docs/en/developing.rst | 47 ++++++++++ tools/mfg_tool/mfg_tool.py | 39 ++++++++ tools/mfg_tool/utils.py | 27 ++++++ 18 files changed, 460 insertions(+), 5 deletions(-) diff --git a/components/esp_matter/CMakeLists.txt b/components/esp_matter/CMakeLists.txt index 6ba75381b..3bb3d30bd 100644 --- a/components/esp_matter/CMakeLists.txt +++ b/components/esp_matter/CMakeLists.txt @@ -1,4 +1,5 @@ set(SRC_DIRS_LIST "." + "${MATTER_SDK_PATH}" "${MATTER_SDK_PATH}/zzz_generated/app-common/app-common/zap-generated/attributes" "${MATTER_SDK_PATH}/src/app" "${MATTER_SDK_PATH}/src/app/server" @@ -61,6 +62,8 @@ set(SRC_DIRS_LIST "." "${MATTER_SDK_PATH}/src/app/clusters/wake-on-lan-server" "${MATTER_SDK_PATH}/src/app/clusters/wifi-network-diagnostics-server" "${MATTER_SDK_PATH}/src/app/clusters/window-covering-server" + "${MATTER_SDK_PATH}/src/app/clusters/mode-select-server" + "${MATTER_SDK_PATH}/examples/platform/esp32/mode-support" ) set(INCLUDE_DIRS_LIST "." diff --git a/components/esp_matter/esp_matter_attribute.cpp b/components/esp_matter/esp_matter_attribute.cpp index e3410fb88..22ccd2166 100644 --- a/components/esp_matter/esp_matter_attribute.cpp +++ b/components/esp_matter/esp_matter_attribute.cpp @@ -2176,5 +2176,41 @@ attribute_t *create_control_mode(cluster_t *cluster, uint8_t value) } /* attribute */ } /* pump_configuration_and_control */ +namespace mode_select { +namespace attribute { + +attribute_t *create_mode_select_description(cluster_t *cluster, char * value, uint16_t length) +{ + return esp_matter::attribute::create(cluster, ModeSelect::Attributes::Description::Id, ATTRIBUTE_FLAG_NONE, esp_matter_char_str(value, length)); +} + +attribute_t *create_standard_namespace(cluster_t *cluster, const nullable value) +{ + return esp_matter::attribute::create(cluster, ModeSelect::Attributes::StandardNamespace::Id, ATTRIBUTE_FLAG_NULLABLE, esp_matter_nullable_enum16(value)); +} + +attribute_t *create_supported_modes(cluster_t *cluster, const uint8_t * value, uint16_t length, uint16_t count) +{ + return esp_matter::attribute::create(cluster, ModeSelect::Attributes::SupportedModes::Id, ATTRIBUTE_FLAG_NONE, esp_matter_array((uint8_t*)value, length, count)); +} + +attribute_t *create_current_mode(cluster_t *cluster, uint8_t value) +{ + return esp_matter::attribute::create(cluster, ModeSelect::Attributes::CurrentMode::Id, ATTRIBUTE_FLAG_NONE, esp_matter_uint8(value)); +} + +attribute_t *create_start_up_mode(cluster_t *cluster, nullable value) +{ + return esp_matter::attribute::create(cluster, ModeSelect::Attributes::StartUpMode::Id, ATTRIBUTE_FLAG_WRITABLE | ATTRIBUTE_FLAG_NULLABLE | ATTRIBUTE_FLAG_NONVOLATILE, esp_matter_nullable_uint8(value)); +} + +attribute_t *create_on_mode(cluster_t *cluster, nullable value) +{ + return esp_matter::attribute::create(cluster, ModeSelect::Attributes::OnMode::Id, ATTRIBUTE_FLAG_WRITABLE | ATTRIBUTE_FLAG_NULLABLE | ATTRIBUTE_FLAG_NONVOLATILE, esp_matter_nullable_uint8(value)); +} + +} /* attribute */ +} /* mode_select */ + } /* cluster */ } /* esp_matter */ diff --git a/components/esp_matter/esp_matter_attribute.h b/components/esp_matter/esp_matter_attribute.h index 69dfb18ec..a0c146c79 100644 --- a/components/esp_matter/esp_matter_attribute.h +++ b/components/esp_matter/esp_matter_attribute.h @@ -523,5 +523,16 @@ attribute_t *create_control_mode(cluster_t *cluster, uint8_t value); } /* attribute */ } /* pump_configuration_and_control */ +namespace mode_select { +namespace attribute { +attribute_t *create_mode_select_description(cluster_t *cluster, char * value, uint16_t length); +attribute_t *create_standard_namespace(cluster_t *cluster, const nullable value); +attribute_t *create_supported_modes(cluster_t *cluster, const uint8_t * value, uint16_t length, uint16_t count); +attribute_t *create_current_mode(cluster_t *cluster, uint8_t value); +attribute_t *create_start_up_mode(cluster_t *cluster, nullable value); +attribute_t *create_on_mode(cluster_t *cluster, nullable value); +} /* attribute */ +} /* mode_select */ + } /* cluster */ } /* esp_matter */ diff --git a/components/esp_matter/esp_matter_attribute_utils.cpp b/components/esp_matter/esp_matter_attribute_utils.cpp index 91b73ec67..d2db0f32b 100644 --- a/components/esp_matter/esp_matter_attribute_utils.cpp +++ b/components/esp_matter/esp_matter_attribute_utils.cpp @@ -319,6 +319,30 @@ esp_matter_attr_val_t esp_matter_nullable_enum8(nullable val) return attr_val; } +esp_matter_attr_val_t esp_matter_enum16(uint16_t val) +{ + esp_matter_attr_val_t attr_val = { + .type = ESP_MATTER_VAL_TYPE_ENUM16, + .val = { + .u16 = val, + }, + }; + return attr_val; +} + +esp_matter_attr_val_t esp_matter_nullable_enum16(nullable val) +{ + esp_matter_attr_val_t attr_val = { + .type = ESP_MATTER_VAL_TYPE_NULLABLE_ENUM16, + }; + if (val.is_null()) { + chip::app::NumericAttributeTraits::SetNull(attr_val.val.u16); + } else { + attr_val.val.u16 = val.value(); + } + return attr_val; +} + esp_matter_attr_val_t esp_matter_bitmap8(uint8_t val) { esp_matter_attr_val_t attr_val = { @@ -622,6 +646,18 @@ static esp_err_t console_set_handler(int argc, char **argv) uint8_t value = atoi(argv[3]); val = esp_matter_enum8(value); } + } else if (type == ESP_MATTER_VAL_TYPE_ENUM16) { + if (matter_attribute->IsNullable()) { + if (strncmp(argv[3], "null", sizeof("null")) == 0) { + val = esp_matter_nullable_enum16(nullable()); + } else { + uint16_t value = atoi(argv[3]); + val = esp_matter_nullable_enum16(value); + } + } else { + uint16_t value = atoi(argv[3]); + val = esp_matter_enum16(value); + } } else { ESP_LOGE(TAG, "Type not handled: %d", type); return ESP_ERR_INVALID_ARG; @@ -834,6 +870,20 @@ static esp_err_t console_get_handler(int argc, char **argv) } else { val = esp_matter_enum8(Traits::StorageToWorking(value)); } + } else if (type == ESP_MATTER_VAL_TYPE_ENUM16) { + using Traits = chip::app::NumericAttributeTraits; + Traits::StorageType value; + uint8_t *read_able = Traits::ToAttributeStoreRepresentation(value); + get_val_raw(endpoint_id, cluster_id, attribute_id, read_able, sizeof(value)); + if (matter_attribute->IsNullable()) { + if (Traits::IsNullValue(value)) { + val = esp_matter_nullable_enum16(nullable()); + } else { + val = esp_matter_nullable_enum16(Traits::StorageToWorking(value)); + } + } else { + val = esp_matter_enum16(Traits::StorageToWorking(value)); + } } else { ESP_LOGE(TAG, "Type not handled: %d", type); return ESP_ERR_INVALID_ARG; @@ -980,6 +1030,10 @@ static esp_matter_val_type_t get_val_type_from_attribute_type(int attribute_type return ESP_MATTER_VAL_TYPE_ENUM8; break; + case ZCL_ENUM16_ATTRIBUTE_TYPE: + return ESP_MATTER_VAL_TYPE_ENUM16; + break; + case ZCL_BITMAP8_ATTRIBUTE_TYPE: return ESP_MATTER_VAL_TYPE_BITMAP8; break; @@ -1011,6 +1065,7 @@ bool val_is_null(esp_matter_attr_val_t *val) return chip::app::NumericAttributeTraits::IsNullValue(val->val.u8); break; case ESP_MATTER_VAL_TYPE_NULLABLE_UINT16: + case ESP_MATTER_VAL_TYPE_NULLABLE_ENUM16: case ESP_MATTER_VAL_TYPE_NULLABLE_BITMAP16: return chip::app::NumericAttributeTraits::IsNullValue(val->val.u16); break; @@ -1300,6 +1355,24 @@ esp_err_t get_data_from_attr_val(esp_matter_attr_val_t *val, EmberAfAttributeTyp } break; + case ESP_MATTER_VAL_TYPE_ENUM16: + case ESP_MATTER_VAL_TYPE_NULLABLE_ENUM16: + if (attribute_type) { + *attribute_type = ZCL_ENUM16_ATTRIBUTE_TYPE; + } + if (attribute_size) { + *attribute_size = sizeof(uint16_t); + } + if (value) { + using Traits = chip::app::NumericAttributeTraits; + if ((val->type & ESP_MATTER_VAL_NULLABLE_BASE) && Traits::IsNullValue(val->val.u16)) { + Traits::SetNull(*(uint16_t *)value); + } else { + Traits::WorkingToStorage(val->val.u16, *(uint16_t *)value); + } + } + break; + case ESP_MATTER_VAL_TYPE_BITMAP8: case ESP_MATTER_VAL_TYPE_NULLABLE_BITMAP8: if (attribute_type) { @@ -1548,6 +1621,22 @@ static esp_err_t get_attr_val_from_data(esp_matter_attr_val_t *val, EmberAfAttri break; } + case ZCL_ENUM16_ATTRIBUTE_TYPE: { + using Traits = chip::app::NumericAttributeTraits; + Traits::StorageType attribute_value; + memcpy((uint16_t *)&attribute_value, value, sizeof(Traits::StorageType)); + if (attribute_metadata->IsNullable()) { + if (Traits::IsNullValue(attribute_value)) { + *val = esp_matter_nullable_enum16(nullable()); + } else { + *val = esp_matter_nullable_enum16(attribute_value); + } + } else { + *val = esp_matter_enum16(attribute_value); + } + break; + } + case ZCL_BITMAP8_ATTRIBUTE_TYPE: { using Traits = chip::app::NumericAttributeTraits; Traits::StorageType attribute_value; @@ -1617,8 +1706,7 @@ void val_print(uint16_t endpoint_id, uint32_t cluster_id, uint32_t attribute_id, } else if (val->type == ESP_MATTER_VAL_TYPE_INT16 || val->type == ESP_MATTER_VAL_TYPE_NULLABLE_INT16) { ESP_LOGI(TAG, "********** %c : Endpoint 0x%04" PRIX16 "'s Cluster 0x%08" PRIX32 "'s Attribute 0x%08" PRIX32 " is %" PRIi16 " **********", action, endpoint_id, cluster_id, attribute_id, val->val.i16); - } else if (val->type == ESP_MATTER_VAL_TYPE_UINT16 || val->type == ESP_MATTER_VAL_TYPE_BITMAP16 - || val->type == ESP_MATTER_VAL_TYPE_NULLABLE_UINT16 || val->type == ESP_MATTER_VAL_TYPE_NULLABLE_BITMAP16) { + } else if (val->type == ESP_MATTER_VAL_TYPE_UINT16 || val->type == ESP_MATTER_VAL_TYPE_BITMAP16 || val->type == ESP_MATTER_VAL_TYPE_ENUM16 || val->type == ESP_MATTER_VAL_TYPE_NULLABLE_UINT16 || val->type == ESP_MATTER_VAL_TYPE_NULLABLE_BITMAP16 || val->type == ESP_MATTER_VAL_TYPE_NULLABLE_ENUM16) { ESP_LOGI(TAG, "********** %c : Endpoint 0x%04" PRIX16 "'s Cluster 0x%08" PRIX32 "'s Attribute 0x%08" PRIX32 " is %" PRIu16 " **********", action, endpoint_id, cluster_id, attribute_id, val->val.u16); } else if (val->type == ESP_MATTER_VAL_TYPE_INT32|| val->type == ESP_MATTER_VAL_TYPE_NULLABLE_INT32) { diff --git a/components/esp_matter/esp_matter_attribute_utils.h b/components/esp_matter/esp_matter_attribute_utils.h index ee16428e6..3b42a1579 100644 --- a/components/esp_matter/esp_matter_attribute_utils.h +++ b/components/esp_matter/esp_matter_attribute_utils.h @@ -72,12 +72,14 @@ typedef enum { ESP_MATTER_VAL_TYPE_UINT64 = 14, /** 8 bit enum */ ESP_MATTER_VAL_TYPE_ENUM8 = 15, + /** 16 bit enum */ + ESP_MATTER_VAL_TYPE_ENUM16 = 16, /** 8 bit bitmap */ - ESP_MATTER_VAL_TYPE_BITMAP8 = 16, + ESP_MATTER_VAL_TYPE_BITMAP8 = 17, /** 16 bit bitmap */ - ESP_MATTER_VAL_TYPE_BITMAP16 = 17, + ESP_MATTER_VAL_TYPE_BITMAP16 = 18, /** 32 bit bitmap */ - ESP_MATTER_VAL_TYPE_BITMAP32 = 18, + ESP_MATTER_VAL_TYPE_BITMAP32 = 19, /** nullable types **/ ESP_MATTER_VAL_TYPE_NULLABLE_INTEGER = ESP_MATTER_VAL_TYPE_INTEGER + ESP_MATTER_VAL_NULLABLE_BASE, ESP_MATTER_VAL_TYPE_NULLABLE_FLOAT = ESP_MATTER_VAL_TYPE_FLOAT + ESP_MATTER_VAL_NULLABLE_BASE, @@ -90,6 +92,7 @@ typedef enum { ESP_MATTER_VAL_TYPE_NULLABLE_INT64 = ESP_MATTER_VAL_TYPE_INT64 + ESP_MATTER_VAL_NULLABLE_BASE, ESP_MATTER_VAL_TYPE_NULLABLE_UINT64 = ESP_MATTER_VAL_TYPE_UINT64 + ESP_MATTER_VAL_NULLABLE_BASE, ESP_MATTER_VAL_TYPE_NULLABLE_ENUM8 = ESP_MATTER_VAL_TYPE_ENUM8 + ESP_MATTER_VAL_NULLABLE_BASE, + ESP_MATTER_VAL_TYPE_NULLABLE_ENUM16 = ESP_MATTER_VAL_TYPE_ENUM16 + ESP_MATTER_VAL_NULLABLE_BASE, ESP_MATTER_VAL_TYPE_NULLABLE_BITMAP8 = ESP_MATTER_VAL_TYPE_BITMAP8 + ESP_MATTER_VAL_NULLABLE_BASE, ESP_MATTER_VAL_TYPE_NULLABLE_BITMAP16 = ESP_MATTER_VAL_TYPE_BITMAP16 + ESP_MATTER_VAL_NULLABLE_BASE, ESP_MATTER_VAL_TYPE_NULLABLE_BITMAP32= ESP_MATTER_VAL_TYPE_BITMAP32 + ESP_MATTER_VAL_NULLABLE_BASE, @@ -277,6 +280,10 @@ esp_matter_attr_val_t esp_matter_nullable_uint64(nullable val); esp_matter_attr_val_t esp_matter_enum8(uint8_t val); esp_matter_attr_val_t esp_matter_nullable_enum8(nullable val); +/** 16 bit enum */ +esp_matter_attr_val_t esp_matter_enum16(uint16_t val); +esp_matter_attr_val_t esp_matter_nullable_enum16(nullable val); + /** 8 bit bitmap */ esp_matter_attr_val_t esp_matter_bitmap8(uint8_t val); esp_matter_attr_val_t esp_matter_nullable_bitmap8(nullable val); diff --git a/components/esp_matter/esp_matter_cluster.cpp b/components/esp_matter/esp_matter_cluster.cpp index 617f08588..9cb1fea10 100644 --- a/components/esp_matter/esp_matter_cluster.cpp +++ b/components/esp_matter/esp_matter_cluster.cpp @@ -1760,6 +1760,54 @@ cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags) return cluster; } } /* pump_configuration_and_control */ + +namespace mode_select { +const function_generic_t function_list[] = { + (function_generic_t)emberAfModeSelectClusterServerInitCallback, +}; +const int function_flags = CLUSTER_FLAG_INIT_FUNCTION; + +cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags, uint32_t features) +{ + cluster_t *cluster = cluster::create(endpoint, ModeSelect::Id, flags); + if (!cluster) { + ESP_LOGE(TAG, "Could not create cluster"); + return NULL; + } + if (flags & CLUSTER_FLAG_SERVER) { + set_plugin_server_init_callback(cluster, MatterModeSelectPluginServerInitCallback); + add_function_list(cluster, function_list, function_flags); + } + if (flags & CLUSTER_FLAG_CLIENT) { + create_default_binding_cluster(endpoint); + } + + if (flags & CLUSTER_FLAG_SERVER) { + /* Attributes managed internally */ + global::attribute::create_feature_map(cluster, 0); + attribute::create_supported_modes(cluster, NULL, 0, 0); + /** Attributes not managed internally **/ + if (config) { + global::attribute::create_cluster_revision(cluster, config->cluster_revision); + attribute::create_mode_select_description(cluster, config->mode_select_description, sizeof(config->mode_select_description)); + attribute::create_standard_namespace(cluster, config->standard_namespace); + attribute::create_current_mode(cluster, config->current_mode); + } else { + ESP_LOGE(TAG, "Config is NULL. Cannot add some attributes."); + } + } + + /* Commands */ + command::create_change_to_mode(cluster); + + /* Features */ + if (features & feature::dep_on_off::get_id()) { + feature::dep_on_off::add(cluster, &(config->dep_on_off)); + } + return cluster; +} +} /* mode_select */ + #endif /* CONFIG_ESP_MATTER_ENABLE_DATA_MODEL */ } /* cluster */ } /* esp_matter */ diff --git a/components/esp_matter/esp_matter_cluster.h b/components/esp_matter/esp_matter_cluster.h index 8fbb364ca..9afb584ac 100644 --- a/components/esp_matter/esp_matter_cluster.h +++ b/components/esp_matter/esp_matter_cluster.h @@ -470,5 +470,18 @@ typedef struct config { cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags); } /* pump_configuration_and_control */ +namespace mode_select { +typedef struct config { + uint16_t cluster_revision; + char mode_select_description[64]; + const nullable standard_namespace; + uint8_t current_mode; + feature::dep_on_off::config_t dep_on_off; + config() : cluster_revision(1), mode_select_description{0}, standard_namespace(), current_mode(0) {} +} config_t; + +cluster_t *create(endpoint_t *endpoint, config_t *config, uint8_t flags, uint32_t features); +} /* mode_select */ + } /* cluster */ } /* esp_matter */ diff --git a/components/esp_matter/esp_matter_command.cpp b/components/esp_matter/esp_matter_command.cpp index 06be58504..9036629d5 100644 --- a/components/esp_matter/esp_matter_command.cpp +++ b/components/esp_matter/esp_matter_command.cpp @@ -986,6 +986,16 @@ static esp_err_t esp_matter_command_callback_go_to_tilt_percentage(const Concret return ESP_OK; } +static esp_err_t esp_matter_command_callback_change_to_mode(const ConcreteCommandPath &command_path, TLVReader &tlv_data, void *opaque_ptr) +{ + chip::app::Clusters::ModeSelect::Commands::ChangeToMode::DecodableType command_data; + CHIP_ERROR error = Decode(tlv_data, command_data); + if (error == CHIP_NO_ERROR) { + emberAfModeSelectClusterChangeToModeCallback((CommandHandler *)opaque_ptr, command_path, command_data); + } + return ESP_OK; +} + static esp_err_t esp_matter_command_callback_instance_action(const ConcreteCommandPath &command_path, TLVReader &tlv_data, void *opaque_ptr) { @@ -2010,6 +2020,17 @@ command_t *create_go_to_tilt_percentage(cluster_t *cluster) } /* command */ } /* window_covering */ +namespace mode_select { +namespace command { +command_t *create_change_to_mode(cluster_t *cluster) +{ + return esp_matter::command::create(cluster, ModeSelect::Commands::ChangeToMode::Id, COMMAND_FLAG_ACCEPTED, esp_matter_command_callback_change_to_mode); +} + +} /* command */ +} /* mode_select */ + + } /* cluster */ } /* esp_matter */ diff --git a/components/esp_matter/esp_matter_command.h b/components/esp_matter/esp_matter_command.h index df962a1c4..162c6dabd 100644 --- a/components/esp_matter/esp_matter_command.h +++ b/components/esp_matter/esp_matter_command.h @@ -255,5 +255,11 @@ command_t *create_go_to_tilt_percentage(cluster_t *cluster); } /* command */ } /* window_covering */ +namespace mode_select { +namespace command { +command_t *create_change_to_mode(cluster_t *cluster); +} /* command */ +} /* mode_select */ + } /* cluster */ } /* esp_matter */ diff --git a/components/esp_matter/esp_matter_core.cpp b/components/esp_matter/esp_matter_core.cpp index 656853158..284febdae 100644 --- a/components/esp_matter/esp_matter_core.cpp +++ b/components/esp_matter/esp_matter_core.cpp @@ -2068,6 +2068,21 @@ endpoint_t *get_next(endpoint_t *endpoint) return (endpoint_t *)current_endpoint->next; } +uint16_t get_endpoint_count(node_t *node) +{ + if (!node) { + ESP_LOGE(TAG, "Node cannot be NULL"); + return 0; + } + uint16_t count = 0; + endpoint_t *endpoint = get_first(node); + while (endpoint) { + count++; + endpoint = get_next(endpoint); + } + return count; +} + uint16_t get_id(endpoint_t *endpoint) { if (!endpoint) { diff --git a/components/esp_matter/esp_matter_core.h b/components/esp_matter/esp_matter_core.h index 63fdf0cf2..eee6df82c 100644 --- a/components/esp_matter/esp_matter_core.h +++ b/components/esp_matter/esp_matter_core.h @@ -201,6 +201,8 @@ endpoint_t *get_first(node_t *node); */ endpoint_t *get_next(endpoint_t *endpoint); + +uint16_t get_endpoint_count(node_t *node); /** Get endpoint ID * * Get the endpoint ID for the endpoint. diff --git a/components/esp_matter/esp_matter_endpoint.cpp b/components/esp_matter/esp_matter_endpoint.cpp index cf4a9cc0c..63386dcb5 100644 --- a/components/esp_matter/esp_matter_endpoint.cpp +++ b/components/esp_matter/esp_matter_endpoint.cpp @@ -911,6 +911,38 @@ endpoint_t *add(endpoint_t *endpoint, config_t *config) } } /** pump **/ +namespace mode_select_device { +uint32_t get_device_type_id() +{ + return ESP_MATTER_MODE_SELECT_DEVICE_TYPE_ID; +} + +uint8_t get_device_type_version() +{ + return ESP_MATTER_MODE_SELECT_DEVICE_TYPE_VERSION; +} + +endpoint_t *create(node_t *node, config_t *config, uint8_t flags, void *priv_data) +{ + endpoint_t *endpoint = endpoint::create(node, flags, priv_data); + return add(endpoint, config); +} + +endpoint_t *add(endpoint_t *endpoint, config_t *config) +{ + if (!endpoint) { + ESP_LOGE(TAG, "Could not create endpoint"); + return NULL; + } + add_device_type(endpoint, get_device_type_id(), get_device_type_version()); + + descriptor::create(endpoint, CLUSTER_FLAG_SERVER); + mode_select::create(endpoint, &(config->mode_select), CLUSTER_FLAG_SERVER, ESP_MATTER_NONE_FEATURE_ID); + + return endpoint; +} +} /** mode_select_device **/ + } /* endpoint */ namespace node { diff --git a/components/esp_matter/esp_matter_endpoint.h b/components/esp_matter/esp_matter_endpoint.h index 4b7ce0422..e512f69a9 100644 --- a/components/esp_matter/esp_matter_endpoint.h +++ b/components/esp_matter/esp_matter_endpoint.h @@ -74,6 +74,8 @@ #define ESP_MATTER_WINDOW_COVERING_DEVICE_TYPE_VERSION 2 #define ESP_MATTER_PUMP_DEVICE_TYPE_ID 0x0303 #define ESP_MATTER_PUMP_DEVICE_TYPE_VERSION 2 +#define ESP_MATTER_MODE_SELECT_DEVICE_TYPE_ID 0x0027 +#define ESP_MATTER_MODE_SELECT_DEVICE_TYPE_VERSION 1 namespace esp_matter { @@ -417,6 +419,17 @@ endpoint_t *create(node_t *node, config_t *config, uint8_t flags, void *priv_dat endpoint_t *add(endpoint_t *endpoint, config_t *config); } /** pump **/ +namespace mode_select_device { +typedef struct config { + cluster::mode_select::config_t mode_select; +} config_t; + +uint32_t get_device_type_id(); +uint8_t get_device_type_version(); +endpoint_t *create(node_t *node, config_t *config, uint8_t flags, void *priv_data); +endpoint_t *add(endpoint_t *endpoint, config_t *config); +} /** mode_select_device **/ + } /* endpoint */ namespace node { diff --git a/components/esp_matter/esp_matter_feature.cpp b/components/esp_matter/esp_matter_feature.cpp index 9e5b8ad3b..8c68905d1 100644 --- a/components/esp_matter/esp_matter_feature.cpp +++ b/components/esp_matter/esp_matter_feature.cpp @@ -986,5 +986,34 @@ esp_err_t add(cluster_t *cluster, config_t *config) } /* feature */ } /* time_format_localization */ +namespace mode_select { +namespace feature { + +namespace dep_on_off { + +uint32_t get_id() +{ + // enum class for DepOnOff is not present in the upstream code. + // Return the code according to the SPEC + return 0x00; +} + +esp_err_t add(cluster_t *cluster, config_t *config) +{ + if (!cluster) { + ESP_LOGE(TAG, "Cluster cannot be NULL"); + return ESP_ERR_INVALID_ARG; + } + update_feature_map(cluster, get_id()); + + attribute::create_on_mode(cluster, config->on_mode); + + return ESP_OK; +} + +} /* dep_on_off */ + +} /* feature */ +} /* mode_select */ } /* cluster */ } /* esp_matter */ diff --git a/components/esp_matter/esp_matter_feature.h b/components/esp_matter/esp_matter_feature.h index 829d208d9..f09ef0867 100644 --- a/components/esp_matter/esp_matter_feature.h +++ b/components/esp_matter/esp_matter_feature.h @@ -422,5 +422,23 @@ esp_err_t add(cluster_t *cluster, config_t *config); } /* feature */ } /* time_format_localization */ +namespace mode_select { +namespace feature { + +namespace dep_on_off { + +typedef struct config { + nullable on_mode; + config() : on_mode() {} +} config_t; + +uint32_t get_id(); +esp_err_t add(cluster_t *cluster, config_t *config); + +} /* dep_on_off */ + +} /* feature */ +} /* mode_select */ + } /* cluster */ } /* esp_matter */ diff --git a/docs/en/developing.rst b/docs/en/developing.rst index ccc17b79b..6de5e806c 100644 --- a/docs/en/developing.rst +++ b/docs/en/developing.rst @@ -1201,3 +1201,50 @@ Please follow the steps below to enable and use encrypted application images for NOTE: There are several ways to store the private key, such as hardcoding it in the firmware, embedding it as a text file, or reading it from the NVS. We have demonstrated the use of the private key by embedding it as a text file in the light example. + +2.7.2 Mode Select +================== + +This cluster provides an interface for controlling a characteristic of a device that can be set to one of several predefined values. For example, the light pattern of a disco ball, the mode of a massage chair, or the wash cycle of a laundry machine. + +2.7.2.1 Attribute Supported Modes +--------------------------------- + +This attribute is the list of supported modes that may be selected for the CurrentMode attribute. Each item in this list represents a unique mode as indicated by the Mode field of the ModeOptionStruct. Each entry in this list SHALL have a unique value for the Mode field. +ESP_MATTER uses factory partition to set the values of Supported Modes attribute. + +2.7.2.2 Generate Factory Partition Using mfg_tool +------------------------------------------------- + +Use `mfg_tool `__ to generate factory partition of the supported modes attribute. + +2.7.2.2.1 Usage +--------------- + +:: + + cd tools/mfg_tool + ./mfg_tool.py -cn "My bulb" -v 0xFFF2 -p 0x8001 --pai \ + -k path/to/esp-matter/connectedhomeip/connectedhomeip/credentials/test/attestation/Chip-Test-PAI-FFF2-8001-Key.pem \ + -c path/to/esp-matter/connectedhomeip/connectedhomeip/credentials/test/attestation/Chip-Test-PAI-FFF2-8001-Cert.pem \ + -cd path/to/esp-matter/connectedhomeip/connectedhomeip/credentials/test/certification-declaration/Chip-Test-CD-FFF2-8001.der \ + --supported-modes mode1/label1/endpointId/"value\\mfgCode, value\\mfgCode" mode2/label2/endpointId/"value\\mfgCode, value\\mfgCode" + +2.7.2.3 Build example +--------------------- + +For example we want to use mode_select cluster in light example. + +- In file example/light/app_main.cpp. + +:: + #include + { + + cluster::mode_select::config_t ms_config; + cluster_t *ms_cluster = cluster::mode_select::create(endpoint, &ms_config, CLUSTER_FLAG_SERVER, ESP_MATTER_NONE_FEATURE_ID); + + ModeSelect::StaticSupportedModesManager obj; + obj.InitEndpointArray(get_endpoint_count(node)); + + } diff --git a/tools/mfg_tool/mfg_tool.py b/tools/mfg_tool/mfg_tool.py index c0d00cfed..4e56f8da2 100755 --- a/tools/mfg_tool/mfg_tool.py +++ b/tools/mfg_tool/mfg_tool.py @@ -524,6 +524,9 @@ def get_args(): g_dev_inst.add_argument('--fixed-labels', nargs='+', help='List of fixed labels, eg: "0/orientation/up" "1/orientation/down" "2/orientation/down"') + g_dev_inst.add_argument('--supported-modes', type=str, nargs='+', required=False, + help='List of supported modes, eg: mode1/label1/ep/"tagValue1\\mfgCode, tagValue2\\mfgCode" mode2/label2/ep/"tagValue1\\mfgCode, tagValue2\\mfgCode" mode3/label3/ep/"tagValue1\\mfgCode, tagValue2\\mfgCode"') + g_basic = parser.add_argument_group('Few more Basic clusters options') g_basic.add_argument('--product-label', help='Product label') g_basic.add_argument('--product-url', help='Product URL') @@ -598,6 +601,42 @@ def add_optional_KVs(args): chip_factory_append('fl-k/{:x}/{:x}'.format(int(key), i), 'data', 'string', list(entry.keys())[0]) chip_factory_append('fl-v/{:x}/{:x}'.format(int(key), i), 'data', 'string', list(entry.values())[0]) + # SupportedModes are stored as multiple entries + # - sm-sz/ : number of supported modes for the endpoint + # - sm-label// : supported modes label key for the endpoint and index + # - sm-mode// : supported modes mode key for the endpoint and index + # - sm-st-sz// : supported modes SemanticTag key for the endpoint and index + # - st-v/// : semantic tag value key for the endpoint and index and ind + # - st-mfg/// : semantic tag mfg code key for the endpoint and index and ind + if (args.supported_modes is not None): + dictionary = get_supported_modes_dict(args.supported_modes) + for ep in dictionary.keys(): + chip_factory_append('sm-sz/{:x}'.format(int(ep)), 'data', 'u32', len(dictionary[ep])) + + for i in range(len(dictionary[ep])): + item = dictionary[ep][i] + + chip_factory_append('sm-label/{:x}/{:x}'.format(int(ep), i), 'data', 'string', item["Label"]) + chip_factory_append('sm-mode/{:x}/{:x}'.format(int(ep), i), 'data', 'u32', item["Mode"]) + chip_factory_append('sm-st-sz/{:x}/{:x}'.format(int(ep), i), 'data', 'u32', len(item["Semantic_Tag"])) + + for j in range(len(item["Semantic_Tag"])): + entry = item["Semantic_Tag"][j] + + _value = { + 'type': 'data', + 'encoding': 'u32', + 'value': entry["value"] + } + _mfg_code = { + 'type': 'data', + 'encoding': 'u32', + 'value': entry["mfgCode"] + } + + chip_factory_append('st-v/{:x}/{:x}/{:x}'.format(int(ep), i, j), 'data', 'u32', entry["value"]) + chip_factory_append('st-mfg/{:x}/{:x}/{:x}'.format(int(ep), i, j), 'data', 'u32', entry["mfgCode"]) + # Keys from basic clusters if args.product_label is not None: chip_factory_append('product-label', 'data', 'string', args.product_label) diff --git a/tools/mfg_tool/utils.py b/tools/mfg_tool/utils.py index 6e232818e..6e32369e4 100644 --- a/tools/mfg_tool/utils.py +++ b/tools/mfg_tool/utils.py @@ -234,6 +234,33 @@ def get_fixed_label_dict(fixed_labels): return fl_dict +# get_supported_modes_dict() converts the list of strings to per endpoint dictionaries. +# example input : ['0/label1/1/"1\0x8000, 2\0x8000", 1/label2/1/"1\0x8000, 2\0x8000"'] +# example outout : {'1': [{'Label': 'label1', 'Mode': 0, 'Semantic_Tag': [{'value': 1, 'mfgCode': 32768}, {'value': 2, 'mfgCode': 32768}]}, {'Label': 'label2', 'Mode': 1, 'Semantic_Tag': [{'value': 1, 'mfgCode': 32768}, {'value': 2, 'mfgCode': 32768}]}]} + + +def get_supported_modes_dict(supported_modes): + output_dict = {} + + for mode_str in supported_modes: + mode_label_strs = mode_str.split('/') + mode = mode_label_strs[0] + label = mode_label_strs[1] + ep = mode_label_strs[2] + + semantic_tag_strs = mode_label_strs[3].split(', ') + semantic_tags = [{"value": int(v.split('\\')[0]), "mfgCode": int(v.split('\\')[1], 16)} for v in semantic_tag_strs] + + mode_dict = {"Label": label, "Mode": int(mode), "Semantic_Tag": semantic_tags} + + if ep in output_dict: + output_dict[ep].append(mode_dict) + else: + output_dict[ep] = [mode_dict] + + return output_dict + + # Convert the certificate in PEM format to DER format def convert_x509_cert_from_pem_to_der(pem_file, out_der_file): with open(pem_file, 'rb') as f: