// Copyright 2022 Espressif Systems (Shanghai) PTE LTD // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #if CONFIG_ESP_MATTER_COMMISSIONER_ENABLE #include #else #include #endif #include #include #include #include #include #include #include #include #include using namespace chip::app::Clusters; using namespace esp_matter::cluster; static const char *TAG = "cluster_command"; namespace esp_matter { namespace cluster { template esp_err_t decode_command_response(const ConcreteCommandPath &command_path, TLVReader *reader) { ESP_RETURN_ON_FALSE(reader, ESP_ERR_INVALID_ARG, TAG, "reader cannot be NULL"); ESP_RETURN_ON_FALSE(command_path.mClusterId == CommandResponseObjectT::GetClusterId() && command_path.mCommandId == CommandResponseObjectT::GetCommandId(), ESP_ERR_INVALID_ARG, TAG, "Wrong command to decode"); CommandResponseObjectT response; ESP_RETURN_ON_FALSE(chip::app::DataModel::Decode(*reader, response) == CHIP_NO_ERROR, ESP_FAIL, TAG, "Failed to decode response "); char header[64] = {0}; snprintf(header, 64, "cluster-0x%" PRIX32 ", command-0x%" PRIX32 " response:", command_path.mClusterId, command_path.mCommandId); DataModelLogger::LogValue(header, 1, response); return ESP_OK; } namespace group_key_management { namespace command { void decode_response(const ConcreteCommandPath &command_path, TLVReader *reader) { if (command_path.mCommandId == GroupKeyManagement::Commands::KeySetReadResponse::Id) { decode_command_response(command_path, reader); } } } // namespace command } // namespace group_key_management namespace groups { namespace command { void decode_response(const ConcreteCommandPath &command_path, TLVReader *reader) { if (command_path.mCommandId == Groups::Commands::AddGroupResponse::Id) { decode_command_response(command_path, reader); } else if (command_path.mCommandId == Groups::Commands::ViewGroupResponse::Id) { decode_command_response(command_path, reader); } else if (command_path.mCommandId == Groups::Commands::RemoveGroupResponse::Id) { decode_command_response(command_path, reader); } } } // namespace command } // namespace groups namespace scenes_management { namespace command { void decode_response(const ConcreteCommandPath &command_path, TLVReader *reader) { if (command_path.mCommandId == ScenesManagement::Commands::AddSceneResponse::Id) { decode_command_response(command_path, reader); } else if (command_path.mCommandId == ScenesManagement::Commands::ViewSceneResponse::Id) { decode_command_response(command_path, reader); } else if (command_path.mCommandId == ScenesManagement::Commands::RemoveSceneResponse::Id) { decode_command_response(command_path, reader); } else if (command_path.mCommandId == ScenesManagement::Commands::RemoveAllScenesResponse::Id) { decode_command_response(command_path, reader); } else if (command_path.mCommandId == ScenesManagement::Commands::StoreSceneResponse::Id) { decode_command_response(command_path, reader); } else if (command_path.mCommandId == ScenesManagement::Commands::GetSceneMembershipResponse::Id) { decode_command_response(command_path, reader); } } } // namespace command } // namespace scenes_management namespace thermostat { namespace command { void decode_response(const ConcreteCommandPath &command_path, TLVReader *reader) { if (command_path.mCommandId == Thermostat::Commands::GetWeeklyScheduleResponse::Id) { decode_command_response(command_path, reader); } } } // namespace command } // namespace thermostat namespace door_lock { namespace command { void decode_response(const ConcreteCommandPath &command_path, TLVReader *reader) { if (command_path.mCommandId == DoorLock::Commands::GetWeekDayScheduleResponse::Id) { decode_command_response(command_path, reader); } else if (command_path.mCommandId == DoorLock::Commands::GetYearDayScheduleResponse::Id) { decode_command_response(command_path, reader); } else if (command_path.mCommandId == DoorLock::Commands::GetHolidayScheduleResponse::Id) { decode_command_response(command_path, reader); } else if (command_path.mCommandId == DoorLock::Commands::GetUserResponse::Id) { decode_command_response(command_path, reader); } else if (command_path.mCommandId == DoorLock::Commands::SetCredentialResponse::Id) { decode_command_response(command_path, reader); } else if (command_path.mCommandId == DoorLock::Commands::GetCredentialStatusResponse::Id) { decode_command_response(command_path, reader); } } } // namespace command } // namespace door_lock } // namespace cluster namespace controller { void cluster_command::on_device_connected_fcn(void *context, ExchangeManager &exchangeMgr, const SessionHandle &sessionHandle) { cluster_command *cmd = reinterpret_cast(context); chip::OperationalDeviceProxy device_proxy(&exchangeMgr, sessionHandle); chip::app::CommandPathParams command_path = {cmd->m_endpoint_id, 0, cmd->m_cluster_id, cmd->m_command_id, chip::app::CommandPathFlags::kEndpointIdValid}; custom::command::send_command(context, &device_proxy, command_path, cmd->m_command_data_field, cmd->on_success_cb, cmd->on_error_cb, chip::NullOptional); chip::Platform::Delete(cmd); return; } void cluster_command::on_device_connection_failure_fcn(void *context, const ScopedNodeId &peerId, CHIP_ERROR error) { cluster_command *cmd = reinterpret_cast(context); chip::Platform::Delete(cmd); return; } void cluster_command::default_success_fcn(void *ctx, const ConcreteCommandPath &command_path, const StatusIB &status, TLVReader *response_data) { ESP_LOGI(TAG, "Send command success"); ESP_LOGI(TAG, "Some commands of specific clusters will have a reponse which is not NullObject, so we need to handle the " "response data for those commands. Here we print the reponse data."); ESP_LOGI(TAG, "If your command's reponse is not printed here, please register another success callback when creating " "the cluster_command object to handle the reponse data."); switch (command_path.mClusterId) { case GroupKeyManagement::Id: cluster::group_key_management::command::decode_response(command_path, response_data); break; case Groups::Id: cluster::groups::command::decode_response(command_path, response_data); break; case ScenesManagement::Id: cluster::scenes_management::command::decode_response(command_path, response_data); break; case Thermostat::Id: cluster::thermostat::command::decode_response(command_path, response_data); break; case DoorLock::Id: cluster::door_lock::command::decode_response(command_path, response_data); break; default: break; } } void cluster_command::default_error_fcn(void *ctx, CHIP_ERROR error) { ESP_LOGI(TAG, "Send command failure: err :%" CHIP_ERROR_FORMAT, error.Format()); } esp_err_t cluster_command::dispatch_group_command(void *context) { esp_err_t err = ESP_OK; cluster_command *cmd = reinterpret_cast(context); uint16_t group_id = cmd->m_destination_id & 0xFFFF; #if CONFIG_ESP_MATTER_COMMISSIONER_ENABLE uint8_t fabric_index = commissioner::get_device_commissioner()->GetFabricIndex(); #else uint8_t fabric_index = get_fabric_index(); #endif chip::app::CommandPathParams command_path = {cmd->m_endpoint_id, group_id, cmd->m_cluster_id, cmd->m_command_id, chip::app::CommandPathFlags::kGroupIdValid}; err = custom::command::send_group_command(fabric_index, command_path, cmd->m_command_data_field); chip::Platform::Delete(cmd); return err; } esp_err_t cluster_command::send_command() { if (is_group_command()) { return dispatch_group_command(reinterpret_cast(this)); } #if CONFIG_ESP_MATTER_COMMISSIONER_ENABLE if (CHIP_NO_ERROR == commissioner::get_device_commissioner()->GetConnectedDevice(m_destination_id, &on_device_connected_cb, &on_device_connection_failure_cb)) { return ESP_OK; } #else chip::Server *server = &(chip::Server::GetInstance()); server->GetCASESessionManager()->FindOrEstablishSession(ScopedNodeId(m_destination_id, get_fabric_index()), &on_device_connected_cb, &on_device_connection_failure_cb); return ESP_OK; #endif chip::Platform::Delete(this); return ESP_FAIL; } esp_err_t send_invoke_cluster_command(uint64_t destination_id, uint16_t endpoint_id, uint32_t cluster_id, uint32_t command_id, const char *command_data_field) { if (command_data_field && strlen(command_data_field) >= k_command_data_field_buffer_size) { ESP_LOGE(TAG, "The command data field buffer is too small for this command, please increase the buffer size"); return ESP_ERR_INVALID_ARG; } cluster_command *cmd = chip::Platform::New(destination_id, endpoint_id, cluster_id, command_id, command_data_field); if (!cmd) { ESP_LOGE(TAG, "Failed to alloc memory for cluster_command"); return ESP_ERR_NO_MEM; } return cmd->send_command(); } } // namespace controller } // namespace esp_matter