Skip to content

Commit

Permalink
controller: Add support for timed-invoke command and add open enhance…
Browse files Browse the repository at this point in the history
…d commissioning window command
  • Loading branch information
wqx6 committed Jun 7, 2024
1 parent d12e76d commit 0ac4563
Show file tree
Hide file tree
Showing 5 changed files with 419 additions and 94 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

#include <app/server/Server.h>
#include <crypto/CHIPCryptoPAL.h>
#include <lib/core/Optional.h>
#include <setup_payload/ManualSetupPayloadGenerator.h>
#include <setup_payload/QRCodeSetupPayloadGenerator.h>
#include <setup_payload/SetupPayload.h>
Expand Down Expand Up @@ -154,8 +155,8 @@ void cluster_command::on_device_connected_fcn(void *context, ExchangeManager &ex
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};
interaction::invoke::send_request(context, &device_proxy, command_path, cmd->m_command_data_field, cmd->on_success_cb,
cmd->on_error_cb, chip::NullOptional);
interaction::invoke::send_request(context, &device_proxy, command_path, cmd->m_command_data_field,
cmd->on_success_cb, cmd->on_error_cb, cmd->m_timed_invoke_timeout_ms);
chip::Platform::Delete(cmd);
return;
}
Expand Down Expand Up @@ -255,14 +256,15 @@ esp_err_t cluster_command::send_command()
}

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)
uint32_t command_id, const char *command_data_field,
chip::Optional<uint16_t> timed_invoke_timeout_ms)
{
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<cluster_command>(destination_id, endpoint_id, cluster_id, command_id, command_data_field);
cluster_command *cmd = chip::Platform::New<cluster_command>(destination_id, endpoint_id, cluster_id, command_id,
command_data_field, timed_invoke_timeout_ms);
if (!cmd) {
ESP_LOGE(TAG, "Failed to alloc memory for cluster_command");
return ESP_ERR_NO_MEM;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <esp_matter.h>
#include <esp_matter_client.h>
#include <esp_matter_mem.h>
#include <lib/core/Optional.h>

namespace esp_matter {
namespace controller {
Expand All @@ -39,12 +40,14 @@ class cluster_command {
public:
cluster_command(uint64_t destination_id, uint16_t endpoint_id, uint32_t cluster_id, uint32_t command_id,
const char *command_data_field,
const chip::Optional<uint16_t> timed_invoke_timeout_ms = chip::NullOptional,
custom_command_callback::on_success_callback_t on_success = default_success_fcn,
custom_command_callback::on_error_callback_t on_error = default_error_fcn)
: m_destination_id(destination_id)
, m_endpoint_id(endpoint_id)
, m_cluster_id(cluster_id)
, m_command_id(command_id)
, m_timed_invoke_timeout_ms(timed_invoke_timeout_ms)
, on_device_connected_cb(on_device_connected_fcn, this)
, on_device_connection_failure_cb(on_device_connection_failure_fcn, this)
, on_success_cb(on_success)
Expand All @@ -71,6 +74,7 @@ class cluster_command {
uint32_t m_cluster_id;
uint32_t m_command_id;
char m_command_data_field[k_command_data_field_buffer_size];
chip::Optional<uint16_t> m_timed_invoke_timeout_ms;

static void on_device_connected_fcn(void *context, ExchangeManager &exchangeMgr,
const SessionHandle &sessionHandle);
Expand Down Expand Up @@ -101,12 +105,14 @@ class cluster_command {
* @param[in] command_id CommandId
* @param[in] command_data_field Command data string with JSON format
* (https://docs.espressif.com/projects/esp-matter/en/latest/esp32/developing.html#cluster-commands)
* @param[in] timed_invoke_timeout_ms Timeout in millisecond for timed-invoke command
*
* @return ESP_OK on success.
* @return error in case of failure.
*/
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);
uint32_t command_id, const char *command_data_field,
chip::Optional<uint16_t> timed_invoke_timeout_ms = chip::NullOptional);

} // namespace controller
} // namespace esp_matter
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
// Copyright 2024 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 <esp_err.h>
#include <esp_log.h>
#include <esp_matter_controller_client.h>
#include <esp_matter_controller_utils.h>
#include <esp_matter_controller_commissioning_window_opener.h>

#include <app-common/zap-generated/cluster-objects.h>
#include <app/server/Server.h>
#include <controller/CHIPCluster.h>
#include <crypto/CHIPCryptoPAL.h>
#include <lib/core/CHIPError.h>
#include <lib/core/NodeId.h>
#include <lib/core/Optional.h>
#include <setup_payload/ManualSetupPayloadGenerator.h>
#include <setup_payload/QRCodeSetupPayloadGenerator.h>

using namespace chip::app::Clusters;

#define TAG "controller"

namespace esp_matter {
namespace controller {

esp_err_t commissioning_window_opener::send_open_commissioning_window_command(uint64_t node_id, bool is_enhanced,
uint16_t timeout, uint32_t iteration,
uint16_t discriminator,
uint16_t timed_invoke_timeout_ms)
{
if (!chip::IsOperationalNodeId(node_id)) {
return ESP_ERR_INVALID_ARG;
}
m_is_enhanced = is_enhanced;
m_timout = timeout;
m_iteration = iteration;
m_discriminator = discriminator;
m_timed_invoke_timeout_ms = timed_invoke_timeout_ms;
#ifdef CONFIG_ESP_MATTER_ENABLE_MATTER_SERVER
chip::Server &server = chip::Server::GetInstance();
server.GetCASESessionManager()->FindOrEstablishSession(ScopedNodeId(node_id, get_fabric_index()),
&on_device_connected_cb, &on_device_connection_failure_cb);
return ESP_OK;
#else
auto &controller_instance = esp_matter::controller::matter_controller_client::get_instance();
#ifdef CONFIG_ESP_MATTER_COMMISSIONER_ENABLE
if (CHIP_NO_ERROR ==
controller_instance.get_commissioner()->GetConnectedDevice(node_id, &on_device_connected_cb,
&on_device_connection_failure_cb)) {
return ESP_OK;
}
#else
if (CHIP_NO_ERROR ==
controller_instance.get_controller()->GetConnectedDevice(node_id, &on_device_connected_cb,
&on_device_connection_failure_cb)) {
return ESP_OK;
}
#endif // CONFIG_ESP_MATTER_COMMISSIONER_ENABLE
#endif // CONFIG_ESP_MATTER_ENABLE_MATTER_SERVER
return ESP_OK;
}
static esp_err_t generate_pase_verifier(uint32_t iteration, uint32_t &pincode, chip::MutableByteSpan &salt,
chip::Crypto::Spake2pVerifier &verifier)
{
if (chip::Crypto::DRBG_get_bytes(salt.data(), salt.size()) != CHIP_NO_ERROR) {
ESP_LOGE(TAG, "Failed to generate salt");
return ESP_FAIL;
}
if (chip::PASESession::GeneratePASEVerifier(verifier, iteration, salt, true, pincode) != CHIP_NO_ERROR) {
ESP_LOGE(TAG, "Failed to generate PASE verifier");
return ESP_FAIL;
}
return ESP_OK;
}

static esp_err_t generate_manual_code(uint32_t pincode, uint16_t discriminator, char *manual_code_buffer,
size_t buffer_size)
{
chip::SetupPayload payload = chip::SetupPayload();
payload.setUpPINCode = pincode;
payload.version = 0;
payload.discriminator.SetLongValue(discriminator);
payload.rendezvousInformation.SetValue(chip::RendezvousInformationFlag::kOnNetwork);
char payload_buffer[chip::QRCodeBasicSetupPayloadGenerator::kMaxQRCodeBase38RepresentationLength + 1];
chip::MutableCharSpan manual_code(payload_buffer);
CHIP_ERROR err = chip::ManualSetupPayloadGenerator(payload).payloadDecimalStringRepresentation(manual_code);
if (err != CHIP_NO_ERROR || manual_code.size() >= buffer_size) {
return ESP_FAIL;
}
strncpy(manual_code_buffer, manual_code.data(), manual_code.size());
manual_code_buffer[manual_code.size()] = 0;
return ESP_OK;
}

void commissioning_window_opener::on_device_connected_fcn(void *context, ExchangeManager &exchangeMgr,
const SessionHandle &sessionHandle)
{
commissioning_window_opener *window_opener = reinterpret_cast<commissioning_window_opener *>(context);
if (!window_opener) {
return;
}
if (window_opener->m_is_enhanced) {
uint8_t salt_buffer[chip::Crypto::kSpake2p_Max_PBKDF_Salt_Length];
chip::MutableByteSpan salt = chip::MutableByteSpan(salt_buffer);
chip::Crypto::Spake2pVerifier verifier;
if (generate_pase_verifier(window_opener->m_iteration, window_opener->m_pincode, salt, verifier) != ESP_OK) {
return;
}
chip::Spake2pVerifierSerialized serialized_verifier;
chip::MutableByteSpan serialized_verifier_span(serialized_verifier);
if (verifier.Serialize(serialized_verifier_span) != CHIP_NO_ERROR) {
ESP_LOGE(TAG, "Failed to serialize the verifier");
return;
}
AdministratorCommissioning::Commands::OpenCommissioningWindow::Type command_data;
command_data.commissioningTimeout = window_opener->m_timout;
command_data.PAKEPasscodeVerifier = serialized_verifier_span;
command_data.discriminator = window_opener->m_discriminator;
command_data.iterations = window_opener->m_iteration;
command_data.salt = salt;

chip::Controller::ClusterBase cluster(exchangeMgr, sessionHandle, window_opener->m_default_remote_endpoint_id);
cluster.InvokeCommand(command_data, window_opener, send_command_success_callback, send_command_failure_callback,
chip::MakeOptional(window_opener->m_timed_invoke_timeout_ms));
} else {
AdministratorCommissioning::Commands::OpenBasicCommissioningWindow::Type command_data;
command_data.commissioningTimeout = window_opener->m_timout;
chip::Controller::ClusterBase cluster(exchangeMgr, sessionHandle, window_opener->m_default_remote_endpoint_id);
cluster.InvokeCommand(command_data, window_opener, send_command_success_callback, send_command_failure_callback,
chip::MakeOptional(window_opener->m_timed_invoke_timeout_ms));
}
}

void commissioning_window_opener::on_device_connection_failure_fcn(void *context, const ScopedNodeId &peerId,
CHIP_ERROR error)
{
commissioning_window_opener *window_opener = reinterpret_cast<commissioning_window_opener *>(context);
if (window_opener) {
ESP_LOGE(TAG, "Failed to establish CASE session for open %s commisioning window command",
window_opener->m_is_enhanced ? "enhanced" : "basic");
}
}

void commissioning_window_opener::send_command_success_callback(void *context,
const chip::app::DataModel::NullObjectType &data)
{
commissioning_window_opener *window_opener = reinterpret_cast<commissioning_window_opener *>(context);
if (!window_opener) {
return;
}
ESP_LOGI(TAG, "Open %s commissioning window finished", window_opener->m_is_enhanced ? "enhanced" : "basic");
if (window_opener->m_is_enhanced) {
if (window_opener->m_callback) {
char manual_code[22];
if (generate_manual_code(window_opener->m_pincode, window_opener->m_discriminator, manual_code,
sizeof(manual_code)) == ESP_OK) {
window_opener->m_callback(manual_code);
}
}
}
}

void commissioning_window_opener::send_command_failure_callback(void *context, CHIP_ERROR error)
{
commissioning_window_opener *window_opener = reinterpret_cast<commissioning_window_opener *>(context);
if (window_opener) {
ESP_LOGE(TAG, "Failed to send open %s commisioning window command",
window_opener->m_is_enhanced ? "enhanced" : "basic");
}
}

} // namespace controller
} // namespace esp_matter
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright 2024 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.

#pragma once

#include <esp_matter.h>
#include <esp_matter_client.h>
#include <esp_matter_mem.h>
#include <lib/core/ScopedNodeId.h>
#include <messaging/ExchangeMgr.h>
#include <transport/Session.h>

using chip::ScopedNodeId;
using chip::SessionHandle;
using chip::Messaging::ExchangeManager;

namespace esp_matter {
namespace controller {

class commissioning_window_opener {
public:
typedef void (*commissioning_window_open_callback_t)(const char *manual_code);

static commissioning_window_opener &get_instance()
{
static commissioning_window_opener instance;
return instance;
}

void set_callback(commissioning_window_open_callback_t callback) { m_callback = callback; }

esp_err_t send_open_commissioning_window_command(uint64_t node_id, bool is_enhanced, uint16_t timeout,
uint32_t iteration, uint16_t discriminator,
uint16_t timed_invoke_timeout_ms);

uint16_t m_default_remote_endpoint_id = 0;

private:
commissioning_window_opener()
: on_device_connected_cb(on_device_connected_fcn, this)
, on_device_connection_failure_cb(on_device_connection_failure_fcn, this)
{
}
~commissioning_window_opener() {}

static void on_device_connected_fcn(void *context, ExchangeManager &exchangeMgr,
const SessionHandle &sessionHandle);
static void on_device_connection_failure_fcn(void *context, const ScopedNodeId &peerId, CHIP_ERROR error);

static void send_command_success_callback(void *context, const chip::app::DataModel::NullObjectType &data);

static void send_command_failure_callback(void *context, CHIP_ERROR error);

chip::Callback::Callback<chip::OnDeviceConnected> on_device_connected_cb;
chip::Callback::Callback<chip::OnDeviceConnectionFailure> on_device_connection_failure_cb;

uint32_t m_discriminator = 0;
bool m_is_enhanced = false;
uint32_t m_pincode = 0;
uint16_t m_timout = 0;
uint32_t m_iteration = 0;
uint16_t m_timed_invoke_timeout_ms = 0;
commissioning_window_open_callback_t m_callback = nullptr;
};

} // namespace controller
} // namespace esp_matter
Loading

0 comments on commit 0ac4563

Please sign in to comment.