From 1017931f00c07a323c7022b653d93642df048f68 Mon Sep 17 00:00:00 2001 From: Tennessee Carmel-Veilleux Date: Mon, 29 Jan 2024 15:30:10 -0500 Subject: [PATCH] Add BooleanState cluster control to pigweed RPCs (#31745) * [WIP] Adding boolean state sensor control to Chef - Add new RPCs to set/read BooleanState cluster - Update nRF, ESP32 and Linux chef examples to include - Update some docs that were stale - Allow StatusUtils for RPC to deal with CHIP_ERROR Issue #25225 Fixes #31725 * Support BooleanState in RPC console * Restyled by clang-format * Fix ESP32 build * Rename EmberOrChipStatusToPwStatus to ToPwmStatus --------- Co-authored-by: erwinpan1 Co-authored-by: Restyled.io --- examples/chef/NEW_CHEF_DEVICES.md | 6 +- examples/chef/esp32/main/CMakeLists.txt | 13 ++++ examples/chef/linux/BUILD.gn | 2 + examples/chef/nrfconnect/CMakeLists.txt | 13 ++++ examples/common/pigweed/BUILD.gn | 7 ++ .../protos/boolean_state_service.proto | 37 ++++++++++ .../common/pigweed/rpc_console/py/BUILD.gn | 1 + .../rpc_console/py/chip_rpc/console.py | 2 + .../pigweed/rpc_services/BooleanState.h | 72 +++++++++++++++++++ .../rpc_services/internal/StatusUtils.h | 26 ++++++- examples/platform/esp32/Rpc.cpp | 12 ++++ examples/platform/linux/Rpc.cpp | 12 ++++ examples/platform/nrfconnect/Rpc.cpp | 12 ++++ 13 files changed, 210 insertions(+), 5 deletions(-) create mode 100644 examples/common/pigweed/protos/boolean_state_service.proto create mode 100644 examples/common/pigweed/rpc_services/BooleanState.h diff --git a/examples/chef/NEW_CHEF_DEVICES.md b/examples/chef/NEW_CHEF_DEVICES.md index 4f52b29966f415..c2e8538e7b8e5a 100644 --- a/examples/chef/NEW_CHEF_DEVICES.md +++ b/examples/chef/NEW_CHEF_DEVICES.md @@ -94,13 +94,13 @@ Basic device availability should show when running: ### Compilation -This example uses `rootnode_contactsensor_lFAGG1bfRO` for commands. Substitute +This example uses `rootnode_contactsensor_27f76aeaf5` for commands. Substitute your own device for testing newly created devices. ``` ./examples/chef/chef.py \ -t linux \ - -d rootnode_contactsensor_lFAGG1bfRO \ + -d rootnode_contactsensor_27f76aeaf5 \ -b ``` @@ -113,5 +113,5 @@ Where options used are: ### Execution Build will be available in -`examples/chef/linux/out/rootnode_contactsensor_lFAGG1bfRO` (path will vary +`examples/chef/linux/out/rootnode_contactsensor_27f76aeaf5` (path will vary based on platform and device being built) diff --git a/examples/chef/esp32/main/CMakeLists.txt b/examples/chef/esp32/main/CMakeLists.txt index 6c342d07b4fa15..e6ab518f3494a9 100644 --- a/examples/chef/esp32/main/CMakeLists.txt +++ b/examples/chef/esp32/main/CMakeLists.txt @@ -141,6 +141,17 @@ pw_proto_library(attributes_service pw_protobuf.common_proto ) +pw_proto_library(boolean_state_service + SOURCES + ${CHIP_ROOT}/examples/common/pigweed/protos/boolean_state_service.proto + PREFIX + boolean_state_service + STRIP_PREFIX + ${CHIP_ROOT}/examples/common/pigweed/protos + DEPS + pw_protobuf.common_proto +) + pw_proto_library(button_service SOURCES ${CHIP_ROOT}/examples/common/pigweed/protos/button_service.proto @@ -191,6 +202,7 @@ pw_proto_library(wifi_service target_link_libraries(${COMPONENT_LIB} PUBLIC attributes_service.nanopb_rpc + boolean_state_service.nanopb_rpc button_service.nanopb_rpc descriptor_service.nanopb_rpc device_service.nanopb_rpc @@ -215,6 +227,7 @@ target_link_options(${COMPONENT_LIB} target_compile_options(${COMPONENT_LIB} PRIVATE "-DPW_RPC_ATTRIBUTE_SERVICE=1" + "-DPW_RPC_BOOLEAN_STATE_SERVICE=1" "-DPW_RPC_BUTTON_SERVICE=1" "-DPW_RPC_DEVICE_SERVICE=1" "-DPW_RPC_DESCRIPTOR_SERVICE=1" diff --git a/examples/chef/linux/BUILD.gn b/examples/chef/linux/BUILD.gn index fa12a79a91a09f..a25c1f5ffa11ce 100644 --- a/examples/chef/linux/BUILD.gn +++ b/examples/chef/linux/BUILD.gn @@ -69,6 +69,7 @@ executable("${sample_name}") { defines = [ "PW_RPC_ENABLED", "PW_RPC_ATTRIBUTE_SERVICE=1", + "PW_RPC_BOOLEAN_STATE_SERVICE=1", "PW_RPC_BUTTON_SERVICE=1", "PW_RPC_DESCRIPTOR_SERVICE=1", "PW_RPC_DEVICE_SERVICE=1", @@ -95,6 +96,7 @@ executable("${sample_name}") { "$dir_pw_trace_tokenized:trace_rpc_service", "${chip_root}/config/linux/lib/pw_rpc:pw_rpc", "${chip_root}/examples/common/pigweed:attributes_service.nanopb_rpc", + "${chip_root}/examples/common/pigweed:boolean_state_service.nanopb_rpc", "${chip_root}/examples/common/pigweed:button_service.nanopb_rpc", "${chip_root}/examples/common/pigweed:descriptor_service.nanopb_rpc", "${chip_root}/examples/common/pigweed:device_service.nanopb_rpc", diff --git a/examples/chef/nrfconnect/CMakeLists.txt b/examples/chef/nrfconnect/CMakeLists.txt index 1d9ab2fb436a62..17556ef398b27a 100644 --- a/examples/chef/nrfconnect/CMakeLists.txt +++ b/examples/chef/nrfconnect/CMakeLists.txt @@ -133,6 +133,17 @@ pw_proto_library(attributes_service pw_protobuf.common_proto ) +pw_proto_library(boolean_state_service + SOURCES + ${CHIP_ROOT}/examples/common/pigweed/protos/boolean_state_service.proto + PREFIX + boolean_state_service + STRIP_PREFIX + ${CHIP_ROOT}/examples/common/pigweed/protos + DEPS + pw_protobuf.common_proto +) + pw_proto_library(descriptor_service SOURCES ${CHIP_ROOT}/examples/common/pigweed/protos/descriptor_service.proto @@ -202,6 +213,7 @@ target_include_directories(app PRIVATE target_compile_options(app PRIVATE "-DPW_RPC_ATTRIBUTE_SERVICE=1" + "-DPW_RPC_BOOLEAN_STATE_SERVICE=1" "-DPW_RPC_DESCRIPTOR_SERVICE=1" "-DPW_RPC_DEVICE_SERVICE=1" "-DPW_RPC_THREAD_SERVICE=1" @@ -209,6 +221,7 @@ target_compile_options(app PRIVATE target_link_libraries(app PRIVATE attributes_service.nanopb_rpc + boolean_state_service.nanopb_rpc descriptor_service.nanopb_rpc device_service.nanopb_rpc thread_service.nanopb_rpc diff --git a/examples/common/pigweed/BUILD.gn b/examples/common/pigweed/BUILD.gn index d190b07fd85082..e056f2ef228a8a 100644 --- a/examples/common/pigweed/BUILD.gn +++ b/examples/common/pigweed/BUILD.gn @@ -43,6 +43,13 @@ pw_proto_library("attributes_service") { prefix = "attributes_service" } +pw_proto_library("boolean_state_service") { + sources = [ "protos/boolean_state_service.proto" ] + deps = [ "$dir_pw_protobuf:common_protos" ] + strip_prefix = "protos" + prefix = "boolean_state_service" +} + pw_proto_library("device_service") { sources = [ "protos/device_service.proto" ] inputs = [ "protos/device_service.options" ] diff --git a/examples/common/pigweed/protos/boolean_state_service.proto b/examples/common/pigweed/protos/boolean_state_service.proto new file mode 100644 index 00000000000000..ea15c0174f33b9 --- /dev/null +++ b/examples/common/pigweed/protos/boolean_state_service.proto @@ -0,0 +1,37 @@ +syntax = "proto3"; + +import 'pw_protobuf_protos/common.proto'; + +package chip.rpc; + +// This may eventually contain more attributes. +message BooleanStateState { + bool state_value = 1; +} + +message BooleanStateSetRequest { + uint32 endpoint_id = 1; + bool state_value = 2; +} + +message BooleanStateSetResponse { + uint64 event_number = 1; +} + +message BooleanStateGetRequest { + uint32 endpoint_id = 1; +} + +message BooleanStateGetResponse { + BooleanStateState state = 1; +} + +service BooleanState { + // Set will return generated event number (caused by change) if all supported fields are successfully applied, any + // unsupported fields will be ignored. + rpc Set(BooleanStateSetRequest) returns (BooleanStateSetResponse){} + + // Get will populate all of the supported boolean state cluster state fields + // with the current values. + rpc Get(BooleanStateGetRequest) returns (BooleanStateGetResponse){} +} diff --git a/examples/common/pigweed/rpc_console/py/BUILD.gn b/examples/common/pigweed/rpc_console/py/BUILD.gn index 5c74b7b2599544..a050fb64747302 100644 --- a/examples/common/pigweed/rpc_console/py/BUILD.gn +++ b/examples/common/pigweed/rpc_console/py/BUILD.gn @@ -40,6 +40,7 @@ pw_python_package("chip_rpc") { "$dir_pw_system/py", "$dir_pw_tokenizer/py", "${chip_root}/examples/common/pigweed:attributes_service.python", + "${chip_root}/examples/common/pigweed:boolean_state_service.python", "${chip_root}/examples/common/pigweed:button_service.python", "${chip_root}/examples/common/pigweed:descriptor_service.python", "${chip_root}/examples/common/pigweed:device_service.python", diff --git a/examples/common/pigweed/rpc_console/py/chip_rpc/console.py b/examples/common/pigweed/rpc_console/py/chip_rpc/console.py index 6d2b06fd73208f..f5ed5b5ab4d582 100644 --- a/examples/common/pigweed/rpc_console/py/chip_rpc/console.py +++ b/examples/common/pigweed/rpc_console/py/chip_rpc/console.py @@ -47,6 +47,7 @@ # Protos # isort: off from attributes_service import attributes_service_pb2 +from boolean_state_service import boolean_state_service_pb2 from button_service import button_service_pb2 from descriptor_service import descriptor_service_pb2 from device_service import device_service_pb2 @@ -128,6 +129,7 @@ def show_console(device: str, baudrate: int, use_ipython=True, compiled_protos=[ attributes_service_pb2, + boolean_state_service_pb2, button_service_pb2, descriptor_service_pb2, device_service_pb2, diff --git a/examples/common/pigweed/rpc_services/BooleanState.h b/examples/common/pigweed/rpc_services/BooleanState.h new file mode 100644 index 00000000000000..bb4b524459d03d --- /dev/null +++ b/examples/common/pigweed/rpc_services/BooleanState.h @@ -0,0 +1,72 @@ +/* + * + * Copyright (c) 2024 Project CHIP Authors + * All rights reserved. + * + * 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 "app/util/attribute-storage.h" +#include "boolean_state_service/boolean_state_service.rpc.pb.h" +#include "pigweed/rpc_services/internal/StatusUtils.h" +#include +#include +#include + +namespace chip { +namespace rpc { + +class BooleanState final : public pw_rpc::nanopb::BooleanState::Service +{ +public: + virtual ~BooleanState() = default; + + virtual pw::Status Set(const chip_rpc_BooleanStateSetRequest & request, chip_rpc_BooleanStateSetResponse & response) + { + EndpointId endpointId = request.endpoint_id; + bool newState = request.state_value; + + EventNumber eventNumber; + { + DeviceLayer::StackLock lock; + + // Update attribute first, then emit StateChange event only on success. + RETURN_STATUS_IF_NOT_OK(app::Clusters::BooleanState::Attributes::StateValue::Set(endpointId, newState)); + + chip::app::Clusters::BooleanState::Events::StateChange::Type event{ newState }; + RETURN_STATUS_IF_NOT_OK(app::LogEvent(event, endpointId, eventNumber)); + } + + response.event_number = static_cast(eventNumber); + return pw::OkStatus(); + } + + virtual pw::Status Get(const chip_rpc_BooleanStateGetRequest & request, chip_rpc_BooleanStateGetResponse & response) + { + EndpointId endpointId = request.endpoint_id; + bool state_value = false; + + { + DeviceLayer::StackLock lock; + RETURN_STATUS_IF_NOT_OK(app::Clusters::BooleanState::Attributes::StateValue::Get(endpointId, &state_value)); + } + + response.state.state_value = state_value; + return pw::OkStatus(); + } +}; + +} // namespace rpc +} // namespace chip diff --git a/examples/common/pigweed/rpc_services/internal/StatusUtils.h b/examples/common/pigweed/rpc_services/internal/StatusUtils.h index 444759f818efa2..f92ee66afc44bb 100644 --- a/examples/common/pigweed/rpc_services/internal/StatusUtils.h +++ b/examples/common/pigweed/rpc_services/internal/StatusUtils.h @@ -20,12 +20,13 @@ #pragma once #include "app/util/attribute-storage.h" +#include "protocols/interaction_model/StatusCode.h" #include "pw_status/status.h" #define RETURN_STATUS_IF_NOT_OK(expr) \ do \ { \ - pw::Status __status = chip::rpc::EmberStatusToPwStatus(expr); \ + pw::Status __status = chip::rpc::ToPwStatus(expr); \ if (!__status.ok()) \ { \ return __status; \ @@ -35,7 +36,7 @@ namespace chip { namespace rpc { -constexpr pw::Status EmberStatusToPwStatus(EmberAfStatus ember_status) +constexpr pw::Status ToPwStatus(EmberAfStatus ember_status) { switch (ember_status) { @@ -50,5 +51,26 @@ constexpr pw::Status EmberStatusToPwStatus(EmberAfStatus ember_status) } } +constexpr pw::Status ToPwStatus(CHIP_ERROR chip_error_status) +{ + switch (chip_error_status.AsInteger()) + { + case CHIP_NO_ERROR.AsInteger(): + return pw::OkStatus(); + case CHIP_IM_GLOBAL_STATUS(UnsupportedAttribute).AsInteger(): + case CHIP_IM_GLOBAL_STATUS(UnsupportedCommand).AsInteger(): + case CHIP_IM_GLOBAL_STATUS(UnsupportedEndpoint).AsInteger(): + case CHIP_IM_GLOBAL_STATUS(UnsupportedEvent).AsInteger(): + return pw::Status::NotFound(); + case CHIP_IM_GLOBAL_STATUS(UnsupportedAccess).AsInteger(): + return pw::Status::PermissionDenied(); + case CHIP_IM_GLOBAL_STATUS(InvalidAction).AsInteger(): + case CHIP_IM_GLOBAL_STATUS(InvalidCommand).AsInteger(): + return pw::Status::InvalidArgument(); + default: + return pw::Status::Unknown(); + } +} + } // namespace rpc } // namespace chip diff --git a/examples/platform/esp32/Rpc.cpp b/examples/platform/esp32/Rpc.cpp index 57f01b8f60b2ae..61efb842f089b7 100644 --- a/examples/platform/esp32/Rpc.cpp +++ b/examples/platform/esp32/Rpc.cpp @@ -33,6 +33,10 @@ #include "pigweed/rpc_services/Attributes.h" #endif // defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE +#if defined(PW_RPC_BOOLEAN_STATE_SERVICE) && PW_RPC_BOOLEAN_STATE_SERVICE +#include "pigweed/rpc_services/BooleanState.h" +#endif // defined(PW_RPC_BOOLEAN_STATE_SERVICE) && PW_RPC_BOOLEAN_STATE_SERVICE + #if defined(PW_RPC_BUTTON_SERVICE) && PW_RPC_BUTTON_SERVICE #if CONFIG_DEVICE_TYPE_M5STACK #include "ScreenManager.h" @@ -275,6 +279,10 @@ StackType_t sRpcTaskStack[RPC_TASK_STACK_SIZE]; Attributes attributes_service; #endif // defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE +#if defined(PW_RPC_BOOLEAN_STATE_SERVICE) && PW_RPC_BOOLEAN_STATE_SERVICE +BooleanState boolean_state_service; +#endif // defined(PW_RPC_BOOLEAN_STATE_SERVICE) && PW_RPC_BOOLEAN_STATE_SERVICE + #if defined(PW_RPC_BUTTON_SERVICE) && PW_RPC_BUTTON_SERVICE Esp32Button button_service; #endif // defined(PW_RPC_BUTTON_SERVICE) && PW_RPC_BUTTON_SERVICE @@ -309,6 +317,10 @@ void RegisterServices(pw::rpc::Server & server) server.RegisterService(attributes_service); #endif // defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE +#if defined(PW_RPC_BOOLEAN_STATE_SERVICE) && PW_RPC_BOOLEAN_STATE_SERVICE + server.RegisterService(boolean_state_service); +#endif // defined(PW_RPC_BOOLEAN_STATE_SERVICE) && PW_RPC_BOOLEAN_STATE_SERVICE + #if defined(PW_RPC_BUTTON_SERVICE) && PW_RPC_BUTTON_SERVICE server.RegisterService(button_service); #endif // defined(PW_RPC_BUTTON_SERVICE) && PW_RPC_BUTTON_SERVICE diff --git a/examples/platform/linux/Rpc.cpp b/examples/platform/linux/Rpc.cpp index f45df32d750a81..da6b73b9135c13 100644 --- a/examples/platform/linux/Rpc.cpp +++ b/examples/platform/linux/Rpc.cpp @@ -26,6 +26,10 @@ #include "pigweed/rpc_services/Attributes.h" #endif // defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE +#if defined(PW_RPC_BOOLEAN_STATE_SERVICE) && PW_RPC_BOOLEAN_STATE_SERVICE +#include "pigweed/rpc_services/BooleanState.h" +#endif // defined(PW_RPC_BOOLEAN_STATE_SERVICE) && PW_RPC_BOOLEAN_STATE_SERVICE + #if defined(PW_RPC_DESCRIPTOR_SERVICE) && PW_RPC_DESCRIPTOR_SERVICE #include "pigweed/rpc_services/Descriptor.h" #endif // defined(PW_RPC_DESCRIPTOR_SERVICE) && PW_RPC_DESCRIPTOR_SERVICE @@ -68,6 +72,10 @@ namespace { Attributes attributes_service; #endif // defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE +#if defined(PW_RPC_BOOLEAN_STATE_SERVICE) && PW_RPC_BOOLEAN_STATE_SERVICE +BooleanState boolean_state_service; +#endif // defined(PW_RPC_BOOLEAN_STATE_SERVICE) && PW_RPC_BOOLEAN_STATE_SERVICE + #if defined(PW_RPC_DESCRIPTOR_SERVICE) && PW_RPC_DESCRIPTOR_SERVICE Descriptor descriptor_service; #endif // defined(PW_RPC_DESCRIPTOR_SERVICE) && PW_RPC_DESCRIPTOR_SERVICE @@ -90,6 +98,10 @@ void RegisterServices(pw::rpc::Server & server) server.RegisterService(attributes_service); #endif // defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE +#if defined(PW_RPC_BOOLEAN_STATE_SERVICE) && PW_RPC_BOOLEAN_STATE_SERVICE + server.RegisterService(boolean_state_service); +#endif // defined(PW_RPC_BOOLEAN_STATE_SERVICE) && PW_RPC_BOOLEAN_STATE_SERVICE + #if defined(PW_RPC_DESCRIPTOR_SERVICE) && PW_RPC_DESCRIPTOR_SERVICE server.RegisterService(descriptor_service); #endif // defined(PW_RPC_DESCRIPTOR_SERVICE) && PW_RPC_DESCRIPTOR_SERVICE diff --git a/examples/platform/nrfconnect/Rpc.cpp b/examples/platform/nrfconnect/Rpc.cpp index ec21fd7e379f05..8b47215d6a0680 100644 --- a/examples/platform/nrfconnect/Rpc.cpp +++ b/examples/platform/nrfconnect/Rpc.cpp @@ -32,6 +32,10 @@ LOG_MODULE_DECLARE(app, CONFIG_CHIP_APP_LOG_LEVEL); #include "pigweed/rpc_services/Attributes.h" #endif // defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE +#if defined(PW_RPC_BOOLEAN_STATE_SERVICE) && PW_RPC_BOOLEAN_STATE_SERVICE +#include "pigweed/rpc_services/BooleanState.h" +#endif // defined(PW_RPC_BOOLEAN_STATE_SERVICE) && PW_RPC_BOOLEAN_STATE_SERVICE + #if defined(PW_RPC_BUTTON_SERVICE) && PW_RPC_BUTTON_SERVICE #include "pigweed/rpc_services/Button.h" #endif // defined(PW_RPC_BUTTON_SERVICE) && PW_RPC_BUTTON_SERVICE @@ -139,6 +143,10 @@ struct k_thread rpc_thread_data; Attributes attributes_service; #endif // defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE +#if defined(PW_RPC_BOOLEAN_STATE_SERVICE) && PW_RPC_BOOLEAN_STATE_SERVICE +BooleanState boolean_state_service; +#endif // defined(PW_RPC_BOOLEAN_STATE_SERVICE) && PW_RPC_BOOLEAN_STATE_SERVICE + #if defined(PW_RPC_BUTTON_SERVICE) && PW_RPC_BUTTON_SERVICE NrfButton button_service; #endif // defined(PW_RPC_BUTTON_SERVICE) && PW_RPC_BUTTON_SERVICE @@ -177,6 +185,10 @@ void RegisterServices(pw::rpc::Server & server) server.RegisterService(attributes_service); #endif // defined(PW_RPC_ATTRIBUTE_SERVICE) && PW_RPC_ATTRIBUTE_SERVICE +#if defined(PW_RPC_BOOLEAN_STATE_SERVICE) && PW_RPC_BOOLEAN_STATE_SERVICE + server.RegisterService(boolean_state_service); +#endif // defined(PW_RPC_BOOLEAN_STATE_SERVICE) && PW_RPC_BOOLEAN_STATE_SERVICE + #if defined(PW_RPC_BUTTON_SERVICE) && PW_RPC_BUTTON_SERVICE server.RegisterService(button_service); #endif // defined(PW_RPC_BUTTON_SERVICE) && PW_RPC_BUTTON_SERVICE