diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index cbbed03ad5c417..37890df35df53e 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -665,6 +665,15 @@ jobs: && rm -rf out/linux-x64-all-clusters-${BUILD_VARIANT}-tsan-clang-test " + - name: Build linux-x64-all-devices-app + env: + CCACHE_DIR: "${{ github.workspace }}/.ccache" + run: >- + ./scripts/run_in_build_env.sh "./scripts/build/build_examples.py + --target linux-x64-all-devices-app-tsan-clang-test + --pw-command-launcher=ccache build --copy-artifacts-to objdir-clone + && rm -rf out/linux-x64-all-devices-app-tsan-clang-test" + - name: Build linux-x64-lit-icd env: CCACHE_DIR: "${{ github.workspace }}/.ccache" @@ -806,6 +815,7 @@ jobs: run: | echo -n "" >/tmp/test_env.yaml echo "ALL_CLUSTERS_APP: objdir-clone/linux-x64-all-clusters-${BUILD_VARIANT}-tsan-clang-test/chip-all-clusters-app" >> /tmp/test_env.yaml + echo "ALL_DEVICES_APP: objdir-clone/linux-x64-all-devices-app-tsan-clang-test/all-devices-app" >> /tmp/test_env.yaml echo "BRIDGE_APP: objdir-clone/linux-x64-bridge-${BUILD_VARIANT}-tsan-clang-test-unified/chip-bridge-app" >> /tmp/test_env.yaml echo "CHIP_LOCK_APP: objdir-clone/linux-x64-lock-${BUILD_VARIANT}-tsan-clang-test-unified/chip-lock-app" >> /tmp/test_env.yaml echo "CAMERA_APP: objdir-clone/linux-x64-camera/chip-camera-app" >> /tmp/test_env.yaml diff --git a/examples/all-devices-app/all-devices-common/devices/BUILD.gn b/examples/all-devices-app/all-devices-common/devices/BUILD.gn new file mode 100644 index 00000000000000..273449fed25680 --- /dev/null +++ b/examples/all-devices-app/all-devices-common/devices/BUILD.gn @@ -0,0 +1,18 @@ +# Copyright (c) 2025 Project CHIP Authors +# +# 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. + +config("includes") { + # allows includes like "devices/..." + include_dirs = [ ".." ] +} diff --git a/examples/all-devices-app/all-devices-common/devices/boolean-state-sensor/BUILD.gn b/examples/all-devices-app/all-devices-common/devices/boolean-state-sensor/BUILD.gn new file mode 100644 index 00000000000000..43fcc6ccfcaa23 --- /dev/null +++ b/examples/all-devices-app/all-devices-common/devices/boolean-state-sensor/BUILD.gn @@ -0,0 +1,37 @@ +# Copyright (c) 2025 Project CHIP Authors +# +# 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. + +import("//build_overrides/build.gni") +import("//build_overrides/chip.gni") + +source_set("boolean-state-sensor") { + sources = [ + "BooleanStateSensorDevice.cpp", + "BooleanStateSensorDevice.h", + ] + + public_deps = [ + "${chip_root}/examples/all-devices-app/all-devices-common/devices/interface:single-endpoint-device", + "${chip_root}/src/app/clusters/boolean-state-server", + "${chip_root}/src/app/clusters/identify-server", + "${chip_root}/src/data-model-providers/codedriven", + "${chip_root}/src/lib/core:error", + "${chip_root}/src/lib/support", + ] + + public_configs = [ + "${chip_root}/examples/all-devices-app/all-devices-common/devices/:includes", + "${chip_root}/src:includes", + ] +} diff --git a/examples/all-devices-app/all-devices-common/devices/boolean-state-sensor/BooleanStateSensorDevice.cpp b/examples/all-devices-app/all-devices-common/devices/boolean-state-sensor/BooleanStateSensorDevice.cpp new file mode 100644 index 00000000000000..1d4d33adaf94af --- /dev/null +++ b/examples/all-devices-app/all-devices-common/devices/boolean-state-sensor/BooleanStateSensorDevice.cpp @@ -0,0 +1,52 @@ +/* + * + * Copyright (c) 2025 Project CHIP Authors + * + * 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 + +using namespace chip::app::Clusters; + +namespace chip::app { + +CHIP_ERROR BooleanStateSensorDevice::Register(chip::EndpointId endpoint, CodeDrivenDataModelProvider & provider, + EndpointId parentId) +{ + ReturnErrorOnFailure(SingleEndpointRegistration(endpoint, provider, parentId)); + + mIdentifyCluster.Create(IdentifyCluster::Config(endpoint, *mTimerDelegate)); + ReturnErrorOnFailure(provider.AddCluster(mIdentifyCluster.Registration())); + + mBooleanStateCluster.Create(endpoint); + ReturnErrorOnFailure(provider.AddCluster(mBooleanStateCluster.Registration())); + + return provider.AddEndpoint(mEndpointRegistration); +} + +void BooleanStateSensorDevice::UnRegister(CodeDrivenDataModelProvider & provider) +{ + SingleEndpointUnregistration(provider); + if (mBooleanStateCluster.IsConstructed()) + { + provider.RemoveCluster(&mBooleanStateCluster.Cluster()); + mBooleanStateCluster.Destroy(); + } + if (mIdentifyCluster.IsConstructed()) + { + provider.RemoveCluster(&mIdentifyCluster.Cluster()); + mIdentifyCluster.Destroy(); + } +} + +} // namespace chip::app diff --git a/examples/all-devices-app/all-devices-common/devices/boolean-state-sensor/BooleanStateSensorDevice.h b/examples/all-devices-app/all-devices-common/devices/boolean-state-sensor/BooleanStateSensorDevice.h new file mode 100644 index 00000000000000..fad59b0d328395 --- /dev/null +++ b/examples/all-devices-app/all-devices-common/devices/boolean-state-sensor/BooleanStateSensorDevice.h @@ -0,0 +1,56 @@ +/* + * + * Copyright (c) 2025 Project CHIP Authors + * + * 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 +#include +#include +#include +#include + +namespace chip { +namespace app { + +class BooleanStateSensorDevice : public SingleEndpointDevice +{ +public: + /// This is a general class for boolean state sensor devices. The device type passed here will + /// determine the type of sensor it is (contact, water leak, etc.) This is meant to be a reusable + /// class for the sensor types that share the same core functionality through the identify and + /// boolean state clusters. The caller creating a BooleanStateSensorDevice MUST ensure that the underlying + /// data for the Span of deviceTypes remains valid for the entire lifetime of the BooleanStateSensorDevice object instance. + BooleanStateSensorDevice(reporting::ReportScheduler::TimerDelegate * timerDelegate, + Span deviceType) : + SingleEndpointDevice(deviceType), + mTimerDelegate(timerDelegate) + {} + ~BooleanStateSensorDevice() override = default; + + CHIP_ERROR Register(chip::EndpointId endpoint, CodeDrivenDataModelProvider & provider, + EndpointId parentId = kInvalidEndpointId) override; + void UnRegister(CodeDrivenDataModelProvider & provider) override; + + Clusters::BooleanStateCluster & BooleanState() { return mBooleanStateCluster.Cluster(); } + +private: + reporting::ReportScheduler::TimerDelegate * mTimerDelegate; + LazyRegisteredServerCluster mIdentifyCluster; + LazyRegisteredServerCluster mBooleanStateCluster; +}; + +} // namespace app +} // namespace chip diff --git a/examples/all-devices-app/all-devices-common/devices/device-factory/BUILD.gn b/examples/all-devices-app/all-devices-common/devices/device-factory/BUILD.gn new file mode 100644 index 00000000000000..7331b6f1fa307a --- /dev/null +++ b/examples/all-devices-app/all-devices-common/devices/device-factory/BUILD.gn @@ -0,0 +1,31 @@ +# Copyright (c) 2025 Project CHIP Authors +# +# 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. + +import("//build_overrides/build.gni") +import("//build_overrides/chip.gni") + +source_set("device-factory") { + sources = [ "DeviceFactory.h" ] + + public_deps = [ + "${chip_root}/examples/all-devices-app/all-devices-common/devices/boolean-state-sensor", + "${chip_root}/src/lib/core:error", + "${chip_root}/zzz_generated/app-common/devices/", + ] + + public_configs = [ + "${chip_root}/examples/all-devices-app/all-devices-common/devices/:includes", + "${chip_root}/src:includes", + ] +} diff --git a/examples/all-devices-app/all-devices-common/devices/device-factory/DeviceFactory.h b/examples/all-devices-app/all-devices-common/devices/device-factory/DeviceFactory.h new file mode 100644 index 00000000000000..a618c1bfd9986e --- /dev/null +++ b/examples/all-devices-app/all-devices-common/devices/device-factory/DeviceFactory.h @@ -0,0 +1,83 @@ +/* + * + * Copyright (c) 2025 Project CHIP Authors + * + * 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 +#include +#include +#include +#include +#include + +namespace chip::app { + +/** + * This is a factory class made to be used to create any valid device type as part of the + * all-devices-app. This class is meant to abstract away some details of device specific code, + * and to have more generic implementation code being used in main to create a device. The keys + * in the device registry map are the command line arguments used to start the respective device. + * Create devices by fetching the instance of this class and passing in the device type argument + * i.e. DeviceFactory::GetInstance().Create(deviceTypeName) + */ +class DeviceFactory +{ +public: + using DeviceCreator = std::function()>; + + static DeviceFactory & GetInstance() + { + static DeviceFactory instance; + return instance; + } + + bool IsValidDevice(const std::string & deviceTypeArg) { return mRegistry.find(deviceTypeArg) != mRegistry.end(); } + + std::unique_ptr Create(const std::string & deviceTypeArg) + { + if (IsValidDevice(deviceTypeArg)) + { + return mRegistry.find(deviceTypeArg)->second(); + } + else + { + ChipLogError( + Support, + "INTERNAL ERROR: Invalid device type: %s. Run with the --help argument to view the list of valid device types.\n", + deviceTypeArg.c_str()); + } + return nullptr; + } + +private: + std::map mRegistry; + DefaultTimerDelegate timer; + + DeviceFactory() + { + mRegistry["contact-sensor"] = [this]() { + return std::make_unique( + &timer, Span(&Device::Type::kContactSensor, 1)); + }; + mRegistry["water-leak-detector"] = [this]() { + return std::make_unique( + &timer, Span(&Device::Type::kWaterLeakDetector, 1)); + }; + } +}; + +} // namespace chip::app diff --git a/examples/all-devices-app/all-devices-common/devices/interface/BUILD.gn b/examples/all-devices-app/all-devices-common/devices/interface/BUILD.gn new file mode 100644 index 00000000000000..21ae3792493ec3 --- /dev/null +++ b/examples/all-devices-app/all-devices-common/devices/interface/BUILD.gn @@ -0,0 +1,55 @@ +# Copyright (c) 2025 Project CHIP Authors +# +# 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. + +import("//build_overrides/build.gni") +import("//build_overrides/chip.gni") + +source_set("device-interface") { + sources = [ + "DeviceInterface.cpp", + "DeviceInterface.h", + ] + + public_deps = [ + "${chip_root}/src/app/clusters/descriptor", + "${chip_root}/src/data-model-providers/codedriven", + "${chip_root}/src/lib/core:error", + "${chip_root}/src/lib/support", + ] + + public_configs = [ + "${chip_root}/examples/all-devices-app/all-devices-common/devices/:includes", + "${chip_root}/src:includes", + ] +} + +source_set("single-endpoint-device") { + sources = [ + "SingleEndpointDevice.cpp", + "SingleEndpointDevice.h", + ] + + public_deps = [ + ":device-interface", + "${chip_root}/src/app/clusters/descriptor", + "${chip_root}/src/data-model-providers/codedriven", + "${chip_root}/src/lib/core:error", + "${chip_root}/src/lib/support", + ] + + public_configs = [ + "${chip_root}/examples/all-devices-app/all-devices-common/devices/:includes", + "${chip_root}/src:includes", + ] +} diff --git a/examples/all-devices-app/all-devices-common/devices/interface/DeviceInterface.cpp b/examples/all-devices-app/all-devices-common/devices/interface/DeviceInterface.cpp new file mode 100644 index 00000000000000..c4336eba48ebd2 --- /dev/null +++ b/examples/all-devices-app/all-devices-common/devices/interface/DeviceInterface.cpp @@ -0,0 +1,38 @@ +/* + * + * Copyright (c) 2025 Project CHIP Authors + * + * 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 + +using namespace chip::app::Clusters; + +namespace chip::app { + +CHIP_ERROR DeviceInterface::DeviceTypes(ReadOnlyBufferBuilder & out) const +{ + VerifyOrReturnValue(mDescriptorCluster.IsConstructed(), CHIP_NO_ERROR); + ReturnErrorOnFailure(out.ReferenceExisting(mDeviceTypes)); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR DeviceInterface::ClientClusters(ReadOnlyBufferBuilder & out) const +{ + // no bindings + return CHIP_NO_ERROR; +} + +} // namespace chip::app diff --git a/examples/all-devices-app/all-devices-common/devices/interface/DeviceInterface.h b/examples/all-devices-app/all-devices-common/devices/interface/DeviceInterface.h new file mode 100644 index 00000000000000..2a3f669213c904 --- /dev/null +++ b/examples/all-devices-app/all-devices-common/devices/interface/DeviceInterface.h @@ -0,0 +1,68 @@ +/* + * + * Copyright (c) 2025 Project CHIP Authors + * + * 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 +#include +#include +#include +#include + +#include + +namespace chip::app { + +/// A device is an entity that maintains some cluster functionality. +class DeviceInterface : public EndpointInterface +{ +public: + virtual ~DeviceInterface() = default; + + /// Register relevant clusters on the given endpoint. This must only + /// be called once after starting up a device for the first time. This function + /// will create/instantiate all clusters on the device and complete endpoint registration. + /// It should return error if there's any failure when adding the device's clusters to the provider. + /// A parentId of kInvalidEndpointId represents that there is no parent to this device + virtual CHIP_ERROR Register(EndpointId endpoint, CodeDrivenDataModelProvider & provider, + EndpointId parentId = kInvalidEndpointId) = 0; + + /// Removes a device's clusters from the given provider. This + /// must only be called when register has succeeded before. Expected + /// usage of this function is for when the device is no longer needed + /// (for example, on shutown), to destory the device's clusters. + virtual void UnRegister(CodeDrivenDataModelProvider & provider) = 0; + + // Endpoint interface implementation + CHIP_ERROR DeviceTypes(ReadOnlyBufferBuilder & out) const override; + CHIP_ERROR ClientClusters(ReadOnlyBufferBuilder & out) const override; + +protected: + /// The caller creating a DeviceInterface MUST ensure that the underlying data for the Span of + /// deviceTypes remains valid for the entire lifetime of the DeviceInterface object instance. + DeviceInterface(Span deviceTypes) : + mDeviceTypes(deviceTypes), mEndpointRegistration(*this, {}) + {} + + Span mDeviceTypes; + EndpointInterfaceRegistration mEndpointRegistration; + + // Common clusters.. + LazyRegisteredServerCluster mDescriptorCluster; +}; + +} // namespace chip::app diff --git a/examples/all-devices-app/all-devices-common/devices/interface/SingleEndpointDevice.cpp b/examples/all-devices-app/all-devices-common/devices/interface/SingleEndpointDevice.cpp new file mode 100644 index 00000000000000..380bc06eae1ebb --- /dev/null +++ b/examples/all-devices-app/all-devices-common/devices/interface/SingleEndpointDevice.cpp @@ -0,0 +1,53 @@ +/* + * + * Copyright (c) 2025 Project CHIP Authors + * + * 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 + +using namespace chip::app::Clusters; + +namespace chip::app { + +CHIP_ERROR SingleEndpointDevice::SingleEndpointRegistration(EndpointId endpoint, CodeDrivenDataModelProvider & provider, + EndpointId parentId) +{ + VerifyOrReturnError(mEndpointId == kInvalidEndpointId, CHIP_ERROR_INCORRECT_STATE); + mEndpointId = endpoint; + + // TODO: This needs to be updated to be more customizable and allow the cluster to be created with + // optional attributes or semantic tags being set. + mDescriptorCluster.Create(endpoint, DescriptorCluster::OptionalAttributesSet(0), Span()); + ReturnErrorOnFailure(provider.AddCluster(mDescriptorCluster.Registration())); + + mEndpointRegistration.endpointEntry = DataModel::EndpointEntry{ + .id = endpoint, + .parentId = parentId, + .compositionPattern = DataModel::EndpointCompositionPattern::kFullFamily, + }; + return CHIP_NO_ERROR; +} + +void SingleEndpointDevice::SingleEndpointUnregistration(CodeDrivenDataModelProvider & provider) +{ + provider.RemoveEndpoint(mEndpointId); + if (mDescriptorCluster.IsConstructed()) + { + provider.RemoveCluster(&mDescriptorCluster.Cluster()); + mDescriptorCluster.Destroy(); + } +} + +} // namespace chip::app diff --git a/examples/all-devices-app/all-devices-common/devices/interface/SingleEndpointDevice.h b/examples/all-devices-app/all-devices-common/devices/interface/SingleEndpointDevice.h new file mode 100644 index 00000000000000..b25ebc7d38a10c --- /dev/null +++ b/examples/all-devices-app/all-devices-common/devices/interface/SingleEndpointDevice.h @@ -0,0 +1,65 @@ +/* + * + * Copyright (c) 2025 Project CHIP Authors + * + * 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 +#include +#include +#include +#include +#include + +#include + +namespace chip::app { + +/// A device is a entity that maintains some cluster functionality. +/// +/// This implementation assumes that a device is registered on a single +/// endpoint. +class SingleEndpointDevice : public DeviceInterface +{ +public: + virtual ~SingleEndpointDevice() = default; + + EndpointId GetEndpointId() const { return mEndpointId; } + +protected: + /// The caller creating a SingleEndpointDevice MUST ensure that the underlying data for the Span of + /// deviceTypes remains valid for the entire lifetime of the SingleEndpointDevice object instance. + SingleEndpointDevice(Span deviceTypes) : DeviceInterface(deviceTypes) {} + + /// Internal registration function for common device clusters and endpoint registration. + /// Device subclasses are expected to, and must only call this as part of their own device specific Register() + /// functions. This allows creation of common and device-specific clusters to be done together and + /// also to complete endpoint registration. + CHIP_ERROR SingleEndpointRegistration(EndpointId endpoint, CodeDrivenDataModelProvider & provider, EndpointId parentId); + + /// Internal function to unregister a single endpoint device. This will destroy the clusters part of + /// this class, and must be called in a subclass' device-specific UnRegister() function. This allows + /// for the destruction of the general SingleEndpointDevice clusters and device-specifc clusters from + /// the subclass, as well as removal of the device endpoint from the provider to happen together. + void SingleEndpointUnregistration(CodeDrivenDataModelProvider & provider); + + /// A default value of kInvalidEndpointId is used for the endpoint ID. When this is the value of the endpoint ID, + /// it signifies that endpoint registration (and cluster creation) has not yet been completed. The endpoint + /// ID will be set after a call to SingleEndpointRegistration() has been made. + EndpointId mEndpointId = kInvalidEndpointId; +}; + +} // namespace chip::app diff --git a/examples/all-devices-app/all-devices-common/devices/root-node/BUILD.gn b/examples/all-devices-app/all-devices-common/devices/root-node/BUILD.gn new file mode 100644 index 00000000000000..d7976d47c3fba8 --- /dev/null +++ b/examples/all-devices-app/all-devices-common/devices/root-node/BUILD.gn @@ -0,0 +1,46 @@ +# Copyright (c) 2025 Project CHIP Authors +# +# 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. + +import("//build_overrides/build.gni") +import("//build_overrides/chip.gni") + +source_set("root-node") { + sources = [ + "RootNodeDevice.cpp", + "RootNodeDevice.h", + ] + + public_deps = [ + "${chip_root}/examples/all-devices-app/all-devices-common/devices/interface:single-endpoint-device", + "${chip_root}/src/app/clusters/access-control-server", + "${chip_root}/src/app/clusters/administrator-commissioning-server", + "${chip_root}/src/app/clusters/basic-information", + "${chip_root}/src/app/clusters/general-commissioning-server", + "${chip_root}/src/app/clusters/general-diagnostics-server", + "${chip_root}/src/app/clusters/group-key-mgmt-server", + "${chip_root}/src/app/clusters/network-commissioning", + "${chip_root}/src/app/clusters/operational-credentials-server", + "${chip_root}/src/app/clusters/software-diagnostics-server", + "${chip_root}/src/app/clusters/wifi-network-diagnostics-server", + "${chip_root}/src/data-model-providers/codedriven", + "${chip_root}/src/lib/core:error", + "${chip_root}/src/lib/support", + "${chip_root}/zzz_generated/app-common/devices/", + ] + + public_configs = [ + "${chip_root}/examples/all-devices-app/all-devices-common/devices/:includes", + "${chip_root}/src:includes", + ] +} diff --git a/examples/all-devices-app/all-devices-common/devices/root-node/RootNodeDevice.cpp b/examples/all-devices-app/all-devices-common/devices/root-node/RootNodeDevice.cpp new file mode 100644 index 00000000000000..8b3a3cd0b422c3 --- /dev/null +++ b/examples/all-devices-app/all-devices-common/devices/root-node/RootNodeDevice.cpp @@ -0,0 +1,160 @@ +/* + * + * Copyright (c) 2025 Project CHIP Authors + * + * 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 +#include +#include +#include +#include + +using namespace chip; +using namespace chip::app; +using namespace chip::app::Clusters; +using namespace chip::DeviceLayer; + +namespace chip { +namespace app { + +CHIP_ERROR RootNodeDevice::Register(EndpointId endpointId, CodeDrivenDataModelProvider & provider, EndpointId parentId) +{ + ReturnErrorOnFailure(SingleEndpointRegistration(endpointId, provider, parentId)); + + mBasicInformationCluster.Create(); + + // TODO: This needs to be refactored so the optional attributes being set for + // the cluster are configurable to allow different settings + mBasicInformationCluster.Cluster() + .OptionalAttributes() + .Set() + .Set() + .Set() + .Set() + .Set() + .Set() + .Set(); + + ReturnErrorOnFailure(provider.AddCluster(mBasicInformationCluster.Registration())); + + mGeneralCommissioningCluster.Create(); + ReturnErrorOnFailure(provider.AddCluster(mGeneralCommissioningCluster.Registration())); + + mAdministratorCommissioningCluster.Create(endpointId, BitFlags{}); + ReturnErrorOnFailure(provider.AddCluster(mAdministratorCommissioningCluster.Registration())); + + mGeneralDiagnosticsCluster.Create(GeneralDiagnosticsCluster::OptionalAttributeSet{}); + ReturnErrorOnFailure(provider.AddCluster(mGeneralDiagnosticsCluster.Registration())); + + mGroupKeyManagementCluster.Create(); + ReturnErrorOnFailure(provider.AddCluster(mGroupKeyManagementCluster.Registration())); + + mSoftwareDiagnosticsServerCluster.Create(SoftwareDiagnosticsLogic::OptionalAttributeSet{}); + ReturnErrorOnFailure(provider.AddCluster(mSoftwareDiagnosticsServerCluster.Registration())); + + mAccessControlCluster.Create(); + ReturnErrorOnFailure(provider.AddCluster(mAccessControlCluster.Registration())); + + mOperationalCredentialsCluster.Create( + endpointId, + OperationalCredentialsCluster::Context{ .fabricTable = Server::GetInstance().GetFabricTable(), + .failSafeContext = Server::GetInstance().GetFailSafeContext(), + .sessionManager = Server::GetInstance().GetSecureSessionManager(), + .dnssdServer = app::DnssdServer::Instance(), + .commissioningWindowManager = + Server::GetInstance().GetCommissioningWindowManager() }); + ReturnErrorOnFailure(provider.AddCluster(mOperationalCredentialsCluster.Registration())); + + return provider.AddEndpoint(mEndpointRegistration); +} + +void RootNodeDevice::UnRegister(CodeDrivenDataModelProvider & provider) +{ + SingleEndpointUnregistration(provider); + if (mBasicInformationCluster.IsConstructed()) + { + provider.RemoveCluster(&mBasicInformationCluster.Cluster()); + mBasicInformationCluster.Destroy(); + } + if (mGeneralCommissioningCluster.IsConstructed()) + { + provider.RemoveCluster(&mGeneralCommissioningCluster.Cluster()); + mGeneralCommissioningCluster.Destroy(); + } + if (mAdministratorCommissioningCluster.IsConstructed()) + { + provider.RemoveCluster(&mAdministratorCommissioningCluster.Cluster()); + mAdministratorCommissioningCluster.Destroy(); + } + if (mGeneralDiagnosticsCluster.IsConstructed()) + { + provider.RemoveCluster(&mGeneralDiagnosticsCluster.Cluster()); + mGeneralDiagnosticsCluster.Destroy(); + } + if (mGroupKeyManagementCluster.IsConstructed()) + { + provider.RemoveCluster(&mGroupKeyManagementCluster.Cluster()); + mGroupKeyManagementCluster.Destroy(); + } + if (mSoftwareDiagnosticsServerCluster.IsConstructed()) + { + provider.RemoveCluster(&mSoftwareDiagnosticsServerCluster.Cluster()); + mSoftwareDiagnosticsServerCluster.Destroy(); + } + if (mAccessControlCluster.IsConstructed()) + { + provider.RemoveCluster(&mAccessControlCluster.Cluster()); + mAccessControlCluster.Destroy(); + } + if (mOperationalCredentialsCluster.IsConstructed()) + { + provider.RemoveCluster(&mOperationalCredentialsCluster.Cluster()); + mOperationalCredentialsCluster.Destroy(); + } +} + +CHIP_ERROR WifiRootNodeDevice::Register(EndpointId endpointId, CodeDrivenDataModelProvider & provider, EndpointId parentId) +{ + ReturnErrorOnFailure(RootNodeDevice::Register(endpointId, provider, parentId)); + + mWifiDiagnosticsCluster.Create(endpointId, DeviceLayer::GetDiagnosticDataProvider(), + WiFiDiagnosticsServerCluster::OptionalAttributeSet{}, + BitFlags{}); + ReturnErrorOnFailure(provider.AddCluster(mWifiDiagnosticsCluster.Registration())); + + mNetworkCommissioningCluster.Create(endpointId, mWifiDriver); + ReturnErrorOnFailure(provider.AddCluster(mNetworkCommissioningCluster.Registration())); + + return CHIP_NO_ERROR; +} + +void WifiRootNodeDevice::UnRegister(CodeDrivenDataModelProvider & provider) +{ + RootNodeDevice::UnRegister(provider); + if (mNetworkCommissioningCluster.IsConstructed()) + { + provider.RemoveCluster(&mNetworkCommissioningCluster.Cluster()); + mNetworkCommissioningCluster.Destroy(); + } + if (mWifiDiagnosticsCluster.IsConstructed()) + { + provider.RemoveCluster(&mWifiDiagnosticsCluster.Cluster()); + mWifiDiagnosticsCluster.Destroy(); + } +} + +} // namespace app +} // namespace chip diff --git a/examples/all-devices-app/all-devices-common/devices/root-node/RootNodeDevice.h b/examples/all-devices-app/all-devices-common/devices/root-node/RootNodeDevice.h new file mode 100644 index 00000000000000..ae3aece1c56cf5 --- /dev/null +++ b/examples/all-devices-app/all-devices-common/devices/root-node/RootNodeDevice.h @@ -0,0 +1,80 @@ +/* + * + * Copyright (c) 2025 Project CHIP Authors + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace app { + +class RootNodeDevice : public SingleEndpointDevice +{ +public: + ~RootNodeDevice() override = default; + + CHIP_ERROR Register(EndpointId endpoint, CodeDrivenDataModelProvider & provider, + EndpointId parentId = kInvalidEndpointId) override; + void UnRegister(CodeDrivenDataModelProvider & provider) override; + +protected: + // Most implementations require network commissioning, so only subclasses have access to this. + RootNodeDevice() : SingleEndpointDevice(Span(&Device::Type::kRootNode, 1)) {} + +private: + LazyRegisteredServerCluster mBasicInformationCluster; + LazyRegisteredServerCluster mGeneralCommissioningCluster; + LazyRegisteredServerCluster + mAdministratorCommissioningCluster; + LazyRegisteredServerCluster mGeneralDiagnosticsCluster; + LazyRegisteredServerCluster mGroupKeyManagementCluster; + LazyRegisteredServerCluster mSoftwareDiagnosticsServerCluster; + LazyRegisteredServerCluster mAccessControlCluster; + LazyRegisteredServerCluster mOperationalCredentialsCluster; +}; + +class WifiRootNodeDevice : public RootNodeDevice +{ +public: + WifiRootNodeDevice(DeviceLayer::NetworkCommissioning::WiFiDriver * wifiDriver) : RootNodeDevice(), mWifiDriver(wifiDriver) {} + + ~WifiRootNodeDevice() override = default; + + CHIP_ERROR Register(EndpointId endpoint, CodeDrivenDataModelProvider & provider, + EndpointId parentId = kInvalidEndpointId) override; + void UnRegister(CodeDrivenDataModelProvider & provider) override; + +private: + LazyRegisteredServerCluster mNetworkCommissioningCluster; + LazyRegisteredServerCluster mWifiDiagnosticsCluster; + DeviceLayer::NetworkCommissioning::WiFiDriver * mWifiDriver; +}; + +} // namespace app +} // namespace chip diff --git a/examples/all-devices-app/linux/.gn b/examples/all-devices-app/linux/.gn new file mode 100644 index 00000000000000..23cdfb229b946a --- /dev/null +++ b/examples/all-devices-app/linux/.gn @@ -0,0 +1,21 @@ +# Copyright (c) 2025 Project CHIP Authors +# +# 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. + +import("//build_overrides/build.gni") + +# The location of the build configuration file. +buildconfig = "${build_root}/config/BUILDCONFIG.gn" + +# CHIP uses angle bracket includes. +check_system_includes = true diff --git a/examples/all-devices-app/linux/BUILD.gn b/examples/all-devices-app/linux/BUILD.gn new file mode 100644 index 00000000000000..e73e3b9ab4fc1d --- /dev/null +++ b/examples/all-devices-app/linux/BUILD.gn @@ -0,0 +1,63 @@ +# Copyright (c) 2025 Project CHIP Authors +# +# 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. + +import("//build_overrides/chip.gni") +import("//build_overrides/pigweed.gni") + +import("${chip_root}/build/chip/tools.gni") +import("${chip_root}/examples/common/pigweed/rpc_config.gni") +import("${chip_root}/src/app/common_flags.gni") + +import("$dir_pw_build/target_types.gni") + +config("includes") { + include_dirs = [ + ".", + "include", + ] +} + +executable("all-devices-app") { + sources = [ "main.cpp" ] + + deps = [ + "${chip_root}/examples/all-devices-app/all-devices-common/devices/device-factory", + "${chip_root}/examples/all-devices-app/all-devices-common/devices/root-node", + "${chip_root}/examples/all-devices-app/linux/app_options:app-options", + "${chip_root}/examples/common/tracing:commandline", + "${chip_root}/examples/platform/linux:app-main", + "${chip_root}/examples/platform/linux:linux-commissionable-data-provider", + "${chip_root}/examples/providers:all_devices_device_info_provider", + "${chip_root}/src/app/persistence", + "${chip_root}/src/app/persistence:default", + "${chip_root}/src/data-model-providers/codedriven", + "${chip_root}/src/lib", + ] + deps += pw_build_LINK_DEPS + + include_dirs = [ + "include", + "${chip_root}/examples/common", + ] + + output_dir = root_out_dir +} + +group("linux") { + deps = [ ":all-devices-app" ] +} + +group("default") { + deps = [ ":linux" ] +} diff --git a/examples/all-devices-app/linux/app_options/AppOptions.cpp b/examples/all-devices-app/linux/app_options/AppOptions.cpp new file mode 100644 index 00000000000000..a971bbfbff04e3 --- /dev/null +++ b/examples/all-devices-app/linux/app_options/AppOptions.cpp @@ -0,0 +1,78 @@ +/* + * + * Copyright (c) 2025 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. + */ + +#include + +using namespace chip; +using namespace chip::ArgParser; + +// App custom argument handling +constexpr uint16_t kOptionDeviceType = 0xffd0; +constexpr uint16_t kOptionEndpoint = 0xffd1; + +const char * AppOptions::mDeviceTypeName = "contact-sensor"; // defaulting to contact sensor if not specified +chip::EndpointId AppOptions::mDeviceEndpoint = 1; // defaulting to endpoint 1 if not specified + +bool AppOptions::AllDevicesAppOptionHandler(const char * program, OptionSet * options, int identifier, const char * name, + const char * value) +{ + switch (identifier) + { + case kOptionDeviceType: + if (value == nullptr) + { + ChipLogError(Support, "INTERNAL ERROR: No device type value passed in.\n"); + return false; + } + ChipLogProgress(AppServer, "Using the device type of %s", value); + mDeviceTypeName = value; + return true; + case kOptionEndpoint: + if (value == nullptr) + { + ChipLogError(Support, "INTERNAL ERROR: No endpoint ID value passed in.\n"); + return false; + } + mDeviceEndpoint = static_cast(atoi(value)); + ChipLogProgress(AppServer, "Using endpoint %d for the device.", mDeviceEndpoint); + return true; + default: + ChipLogError(Support, "%s: INTERNAL ERROR: Unhandled option: %s\n", program, name); + return false; + } + + return true; +} + +OptionSet * AppOptions::GetOptions() +{ + static OptionDef sAllDevicesAppOptionDefs[] = { + { "device", kArgumentRequired, kOptionDeviceType }, + { "endpoint", kArgumentRequired, kOptionEndpoint }, + }; + + // TODO: This message on supported device types needs to be updated to scale + // better once new devices are added. + static OptionSet sCmdLineOptions = { AllDevicesAppOptionHandler, // handler function + sAllDevicesAppOptionDefs, // array of option definitions + "PROGRAM OPTIONS", // help group + "-d, --device \n" + "-e, --endpoint \n" }; + + return &sCmdLineOptions; +} diff --git a/examples/all-devices-app/linux/app_options/AppOptions.h b/examples/all-devices-app/linux/app_options/AppOptions.h new file mode 100644 index 00000000000000..57307b2b594a1a --- /dev/null +++ b/examples/all-devices-app/linux/app_options/AppOptions.h @@ -0,0 +1,40 @@ +/* + * + * Copyright (c) 2025 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 +#include +#include + +class AppOptions +{ +public: + static chip::ArgParser::OptionSet * GetOptions(); + + static const char * GetDeviceType() { return mDeviceTypeName; } + + static chip::EndpointId GetDeviceEndpoint() { return mDeviceEndpoint; } + +private: + static bool AllDevicesAppOptionHandler(const char * program, chip::ArgParser::OptionSet * options, int identifier, + const char * name, const char * value); + + static const char * mDeviceTypeName; + static chip::EndpointId mDeviceEndpoint; +}; diff --git a/examples/all-devices-app/linux/app_options/BUILD.gn b/examples/all-devices-app/linux/app_options/BUILD.gn new file mode 100644 index 00000000000000..976d325ad9c702 --- /dev/null +++ b/examples/all-devices-app/linux/app_options/BUILD.gn @@ -0,0 +1,38 @@ +# Copyright (c) 2025 Project CHIP Authors +# +# 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. + +import("//build_overrides/build.gni") +import("//build_overrides/chip.gni") + +config("option-includes") { + include_dirs = [ ".." ] +} + +source_set("app-options") { + sources = [ + "AppOptions.cpp", + "AppOptions.h", + ] + + public_deps = [ + "${chip_root}/examples/common/tracing:commandline", + "${chip_root}/examples/platform/linux:app-main", + "${chip_root}/src/lib", + ] + + public_configs = [ + "${chip_root}/src:includes", + ":option-includes", + ] +} diff --git a/examples/all-devices-app/linux/build_overrides b/examples/all-devices-app/linux/build_overrides new file mode 120000 index 00000000000000..e578e73312ebd1 --- /dev/null +++ b/examples/all-devices-app/linux/build_overrides @@ -0,0 +1 @@ +../../build_overrides \ No newline at end of file diff --git a/examples/all-devices-app/linux/main.cpp b/examples/all-devices-app/linux/main.cpp new file mode 100644 index 00000000000000..936fddf88f6b8a --- /dev/null +++ b/examples/all-devices-app/linux/main.cpp @@ -0,0 +1,231 @@ +/* + * + * Copyright (c) 2025 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace chip; +using namespace chip::app; +using namespace chip::Platform; +using namespace chip::DeviceLayer; +using namespace chip::app::Clusters; +using namespace chip::ArgParser; + +namespace { +AppMainLoopImplementation * gMainLoopImplementation = nullptr; + +DeviceLayer::NetworkCommissioning::LinuxWiFiDriver sWiFiDriver; + +AllDevicesExampleDeviceInfoProviderImpl gExampleDeviceInfoProvider; + +// To hold SPAKE2+ verifier, discriminator, passcode +LinuxCommissionableDataProvider gCommissionableDataProvider; + +void StopSignalHandler(int /* signal */) +{ + if (gMainLoopImplementation != nullptr) + { + gMainLoopImplementation->SignalSafeStopMainLoop(); + } + else + { + Server::GetInstance().GenerateShutDownEvent(); + SystemLayer().ScheduleLambda([]() { PlatformMgr().StopEventLoopTask(); }); + } +} + +chip::app::DataModel::Provider * PopulateCodeDrivenDataModelProvider(PersistentStorageDelegate * delegate) +{ + static chip::app::DefaultAttributePersistenceProvider attributePersistenceProvider; + static chip::app::CodeDrivenDataModelProvider dataModelProvider = + chip::app::CodeDrivenDataModelProvider(*delegate, attributePersistenceProvider); + + static WifiRootNodeDevice rootNodeDevice(&sWiFiDriver); + static std::unique_ptr constructedDevice; + + rootNodeDevice.Register(kRootEndpointId, dataModelProvider, kInvalidEndpointId); + constructedDevice = DeviceFactory::GetInstance().Create(AppOptions::GetDeviceType()); + constructedDevice->Register(AppOptions::GetDeviceEndpoint(), dataModelProvider, kInvalidEndpointId); + + return &dataModelProvider; +} + +void RunApplication(AppMainLoopImplementation * mainLoop = nullptr) +{ + gMainLoopImplementation = mainLoop; + + static chip::CommonCaseDeviceServerInitParams initParams; + VerifyOrDie(initParams.InitializeStaticResourcesBeforeServerInit() == CHIP_NO_ERROR); + + initParams.dataModelProvider = PopulateCodeDrivenDataModelProvider(initParams.persistentStorageDelegate); + initParams.operationalServicePort = CHIP_PORT; + initParams.userDirectedCommissioningPort = CHIP_UDC_PORT; + initParams.interfaceId = Inet::InterfaceId::Null(); + + chip::CommandLineApp::TracingSetup tracing_setup; + tracing_setup.EnableTracingFor("json:log"); + + // Init ZCL Data Model and CHIP App Server + CHIP_ERROR err = Server::GetInstance().Init(initParams); + if (err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "Server init failed: %" CHIP_ERROR_FORMAT, err.Format()); + chipDie(); + } + + // Now that the server has started and we are done with our startup logging, + // log our discovery/onboarding information again so it's not lost in the + // noise. + ConfigurationMgr().LogDeviceConfig(); + + chip::PayloadContents payload; + + payload.version = 0; + payload.rendezvousInformation.SetValue(RendezvousInformationFlag::kBLE); + + if (GetCommissionableDataProvider()->GetSetupPasscode(payload.setUpPINCode) != CHIP_NO_ERROR) + { + payload.setUpPINCode = CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE; + } + + uint16_t discriminator = 0; + VerifyOrDie(GetCommissionableDataProvider()->GetSetupDiscriminator(discriminator) == CHIP_NO_ERROR); + payload.discriminator.SetLongValue(discriminator); + + VerifyOrDie(chip::DeviceLayer::GetDeviceInstanceInfoProvider()->GetVendorId(payload.vendorID) == CHIP_NO_ERROR); + VerifyOrDie(chip::DeviceLayer::GetDeviceInstanceInfoProvider()->GetProductId(payload.productID) == CHIP_NO_ERROR); + PrintOnboardingCodes(payload); + + SetDeviceAttestationCredentialsProvider(Credentials::Examples::GetExampleDACProvider()); + + sWiFiDriver.Set5gSupport(true); + + struct sigaction sa = {}; + sa.sa_handler = StopSignalHandler; + sa.sa_flags = SA_RESETHAND; + sigaction(SIGINT, &sa, nullptr); + sigaction(SIGTERM, &sa, nullptr); + + if (mainLoop != nullptr) + { + mainLoop->RunMainLoop(); + } + else + { + DeviceLayer::PlatformMgr().RunEventLoop(); + } + gMainLoopImplementation = nullptr; + + Server::GetInstance().Shutdown(); + DeviceLayer::PlatformMgr().Shutdown(); + tracing_setup.StopTracing(); +} + +void EventHandler(const DeviceLayer::ChipDeviceEvent * event, intptr_t arg) +{ + (void) arg; + if (event->Type == DeviceLayer::DeviceEventType::kCHIPoBLEConnectionEstablished) + { + ChipLogProgress(DeviceLayer, "Receive kCHIPoBLEConnectionEstablished"); + } + else if ((event->Type == chip::DeviceLayer::DeviceEventType::kInternetConnectivityChange)) + { + // Restart the server on connectivity change + DnssdServer::Instance().StartServer(); + } +} + +CHIP_ERROR InitCommissionableDataProvider(LinuxCommissionableDataProvider & provider) +{ + auto discriminator = static_cast(CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR); + chip::Optional discriminatorFromParam = LinuxDeviceOptions::GetInstance().discriminator; + if (discriminatorFromParam.HasValue()) + { + discriminator = discriminatorFromParam.Value(); + } + + const auto setupPasscode = MakeOptional(static_cast(CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE)); + const uint32_t spake2pIterationCount = Crypto::kSpake2p_Min_PBKDF_Iterations; + + Optional> serializedSpake2pVerifier = NullOptional; + Optional> spake2pSalt = NullOptional; + + return provider.Init( // + serializedSpake2pVerifier, // + spake2pSalt, // + spake2pIterationCount, // + setupPasscode, // + discriminator // + ); +} + +CHIP_ERROR Initialize(int argc, char * argv[]) +{ + ChipLogProgress(AppServer, "Initializing..."); + ReturnErrorOnFailure(Platform::MemoryInit()); + ReturnErrorOnFailure(ParseArguments(argc, argv, AppOptions::GetOptions())); + ReturnErrorOnFailure(DeviceLayer::PersistedStorage::KeyValueStoreMgrImpl().Init(CHIP_CONFIG_KVS_PATH)); + ReturnErrorOnFailure(DeviceLayer::PlatformMgr().InitChipStack()); + + ReturnErrorOnFailure(InitCommissionableDataProvider(gCommissionableDataProvider)); + DeviceLayer::SetCommissionableDataProvider(&gCommissionableDataProvider); + DeviceLayer::SetDeviceInfoProvider(&gExampleDeviceInfoProvider); + ConfigurationMgr().LogDeviceConfig(); + + ReturnErrorOnFailure(DeviceLayer::PlatformMgrImpl().AddEventHandler(EventHandler, 0)); + ReturnErrorOnFailure(DeviceLayer::ConnectivityMgr().SetBLEDeviceName(nullptr)); + ReturnErrorOnFailure(DeviceLayer::Internal::BLEMgrImpl().ConfigureBle(0, false)); + ReturnErrorOnFailure(DeviceLayer::ConnectivityMgr().SetBLEAdvertisingEnabled(true)); + + return CHIP_NO_ERROR; +} + +} // namespace + +void ApplicationShutdown() {} + +int main(int argc, char * argv[]) +{ + ChipLogProgress(AppServer, "Initializing"); + + if (CHIP_ERROR err = Initialize(argc, argv); err != CHIP_NO_ERROR) + { + ChipLogError(AppServer, "Initialize() failed: %" CHIP_ERROR_FORMAT, err.Format()); + chipDie(); + } + + ChipLogProgress(AppServer, "Hello from all-devices-app!"); + RunApplication(); + + return 0; +} diff --git a/examples/all-devices-app/linux/third_party/connectedhomeip b/examples/all-devices-app/linux/third_party/connectedhomeip new file mode 120000 index 00000000000000..c866b86874994d --- /dev/null +++ b/examples/all-devices-app/linux/third_party/connectedhomeip @@ -0,0 +1 @@ +../../../.. \ No newline at end of file diff --git a/examples/platform/linux/BUILD.gn b/examples/platform/linux/BUILD.gn index d87f18303928d5..81007fb2138d14 100644 --- a/examples/platform/linux/BUILD.gn +++ b/examples/platform/linux/BUILD.gn @@ -99,6 +99,20 @@ source_set("commodity-metering-test-event-trigger") { sources = [ "${chip_root}/src/app/clusters/commodity-metering-server/CommodityMeteringTestEventTriggerHandler.h" ] } +source_set("linux-commissionable-data-provider") { + sources = [ + "LinuxCommissionableDataProvider.cpp", + "LinuxCommissionableDataProvider.h", + ] + public_deps = [ + "${chip_root}/src/lib/core", + "${chip_root}/src/lib/support", + "${chip_root}/src/platform", + ] + + public_configs = [ ":app-main-config" ] +} + source_set("commodity-tariff-test-event-trigger") { sources = [ "${chip_root}/src/app/clusters/commodity-tariff-server/CommodityTariffTestEventTriggerHandler.h" ] } @@ -115,8 +129,6 @@ source_set("app-main") { "AppMain.h", "CommissionableInit.cpp", "CommissionableInit.h", - "LinuxCommissionableDataProvider.cpp", - "LinuxCommissionableDataProvider.h", "NamedPipeCommands.cpp", "NamedPipeCommands.h", "Options.cpp", @@ -137,6 +149,7 @@ source_set("app-main") { ":electrical-grid-conditions-test-event-trigger", ":energy-evse-test-event-trigger", ":energy-reporting-test-event-trigger", + ":linux-commissionable-data-provider", ":meter-identification-test-event-trigger", ":smco-test-event-trigger", ":software-diagnostics-test-event-trigger", diff --git a/examples/providers/AllDevicesExampleDeviceInfoProviderImpl.cpp b/examples/providers/AllDevicesExampleDeviceInfoProviderImpl.cpp new file mode 100644 index 00000000000000..ddaeead8cc180a --- /dev/null +++ b/examples/providers/AllDevicesExampleDeviceInfoProviderImpl.cpp @@ -0,0 +1,64 @@ +/* + * + * Copyright (c) 2025 Project CHIP Authors + * + * 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 +#include +#include +#include + +#include +#include + +#include + +namespace chip { +namespace DeviceLayer { + +AllDevicesExampleDeviceInfoProviderImpl & AllDevicesExampleDeviceInfoProviderImpl::GetDefaultInstance() +{ + static AllDevicesExampleDeviceInfoProviderImpl sInstance; + return sInstance; +} + +DeviceInfoProvider::SupportedLocalesIterator * AllDevicesExampleDeviceInfoProviderImpl::IterateSupportedLocales() +{ + return chip::Platform::New(); +} + +size_t AllDevicesExampleDeviceInfoProviderImpl::AllDevicesSupportedLocalesIteratorImpl::Count() +{ + // Hardcoded list of locales + // {("en-US")} + + return kNumSupportedLocales; +} + +bool AllDevicesExampleDeviceInfoProviderImpl::AllDevicesSupportedLocalesIteratorImpl::Next(CharSpan & output) +{ + // Hardcoded list of locales + static const char * kAllSupportedLocales[kNumSupportedLocales] = { "en-US" }; + + VerifyOrReturnError(mIndex < kNumSupportedLocales, false); + output = CharSpan::fromCharString(kAllSupportedLocales[mIndex]); + mIndex++; + + return true; +} + +} // namespace DeviceLayer +} // namespace chip diff --git a/examples/providers/AllDevicesExampleDeviceInfoProviderImpl.h b/examples/providers/AllDevicesExampleDeviceInfoProviderImpl.h new file mode 100644 index 00000000000000..355750ff25b6b5 --- /dev/null +++ b/examples/providers/AllDevicesExampleDeviceInfoProviderImpl.h @@ -0,0 +1,54 @@ +/* + * + * Copyright (c) 2025 Project CHIP Authors + * + * 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 +#include +#include +#include + +namespace chip { +namespace DeviceLayer { + +class AllDevicesExampleDeviceInfoProviderImpl : public AllClustersExampleDeviceInfoProviderImpl +{ +public: + AllDevicesExampleDeviceInfoProviderImpl() = default; + ~AllDevicesExampleDeviceInfoProviderImpl() override {} + + // Iterators + SupportedLocalesIterator * IterateSupportedLocales() override; + + static AllDevicesExampleDeviceInfoProviderImpl & GetDefaultInstance(); + +protected: + class AllDevicesSupportedLocalesIteratorImpl : public SupportedLocalesIterator + { + public: + AllDevicesSupportedLocalesIteratorImpl() = default; + size_t Count() override; + bool Next(CharSpan & output) override; + void Release() override { chip::Platform::Delete(this); } + + private: + static constexpr size_t kNumSupportedLocales = 1; + size_t mIndex = 0; + }; +}; + +} // namespace DeviceLayer +} // namespace chip diff --git a/examples/providers/BUILD.gn b/examples/providers/BUILD.gn index 1713432170495b..25714fadc82ec3 100644 --- a/examples/providers/BUILD.gn +++ b/examples/providers/BUILD.gn @@ -52,3 +52,21 @@ static_library("all_clusters_device_info_provider") { public_configs = [ ":include_providers_dir" ] } + +static_library("all_devices_device_info_provider") { + output_name = "libMatterAllDevicesDeviceInfoProviderExample" + output_dir = "${root_out_dir}/lib" + + sources = [ + "AllDevicesExampleDeviceInfoProviderImpl.cpp", + "AllDevicesExampleDeviceInfoProviderImpl.h", + ] + + public_deps = [ + ":all_clusters_device_info_provider", + "${chip_root}/src/lib/support", + "${chip_root}/src/platform", + ] + + public_configs = [ ":include_providers_dir" ] +} diff --git a/scripts/build/build/targets.py b/scripts/build/build/targets.py index 08d30d37180d24..d77eda1602b93c 100755 --- a/scripts/build/build/targets.py +++ b/scripts/build/build/targets.py @@ -107,6 +107,7 @@ def BuildHostTarget(): app=HostApp.RPC_CONSOLE).OnlyIfRe(f'{native_board_name}-'), TargetPart('all-clusters', app=HostApp.ALL_CLUSTERS), TargetPart('all-clusters-minimal', app=HostApp.ALL_CLUSTERS_MINIMAL), + TargetPart('all-devices-app', app=HostApp.ALL_DEVICES_APP), TargetPart('chip-tool', app=HostApp.CHIP_TOOL), TargetPart('thermostat', app=HostApp.THERMOSTAT), # TODO: controllers depending on a datamodel is odd. For now fix compile dependencies on ember. diff --git a/scripts/build/builders/host.py b/scripts/build/builders/host.py index b8ef3498970e05..8216f8083f03a8 100644 --- a/scripts/build/builders/host.py +++ b/scripts/build/builders/host.py @@ -49,6 +49,7 @@ class HostFuzzingType(Enum): class HostApp(Enum): ALL_CLUSTERS = auto() ALL_CLUSTERS_MINIMAL = auto() + ALL_DEVICES_APP = auto() CHIP_TOOL = auto() CHIP_TOOL_DARWIN = auto() THERMOSTAT = auto() @@ -122,6 +123,8 @@ def ExamplePath(self): return 'all-clusters-app/linux' elif self == HostApp.ALL_CLUSTERS_MINIMAL: return 'all-clusters-minimal-app/linux' + elif self == HostApp.ALL_DEVICES_APP: + return 'all-devices-app/linux' elif self == HostApp.CHIP_TOOL: return 'chip-tool' elif self == HostApp.CHIP_TOOL_DARWIN: @@ -214,6 +217,9 @@ def OutputNames(self): elif self == HostApp.ALL_CLUSTERS_MINIMAL: yield 'chip-all-clusters-minimal-app' yield 'chip-all-clusters-minimal-app.map' + elif self == HostApp.ALL_DEVICES_APP: + yield 'all-devices-app' + yield 'all-devices-app.map' elif self == HostApp.CHIP_TOOL: yield 'chip-tool' yield 'chip-tool.map' diff --git a/scripts/build/testdata/all_targets_linux_x64.txt b/scripts/build/testdata/all_targets_linux_x64.txt index 275108fedec75b..b518d77776784c 100644 --- a/scripts/build/testdata/all_targets_linux_x64.txt +++ b/scripts/build/testdata/all_targets_linux_x64.txt @@ -9,7 +9,7 @@ efr32-{brd2601b,brd2605a,brd2703a,brd2704b,brd2708a,brd2911a,brd4186a,brd4186c,b esp32-{c3devkit,devkitc,m5stack,qemu}-{all-clusters,all-clusters-minimal,bridge,energy-gateway,energy-management,light,lock,ota-provider,ota-requestor,ota-requestor,shell,temperature-measurement,tests}[-ipv6only][-rpc][-tracing] genio-lighting-app linux-fake-tests[-asan][-boringssl][-clang][-coverage][-dmalloc][-libfuzzer][-mbedtls][-ossfuzz][-pw-fuzztest][-tsan][-ubsan] -linux-{arm64,x64}-{address-resolve-tool,air-purifier,air-quality-sensor,all-clusters,all-clusters-minimal,bridge,camera,camera-controller,chip-cert,chip-tool,closure,contact-sensor,dishwasher,energy-gateway,energy-management,fabric-admin,fabric-bridge,fabric-sync,java-matter-controller,jf-admin-app,jf-control-app,kotlin-matter-controller,light,light-data-model-no-unique-id,lit-icd,lock,microwave-oven,minmdns,network-manager,ota-provider,ota-requestor,python-bindings,refrigerator,rpc-console,rvc,shell,simulated-app1,simulated-app2,terms-and-conditions,tests,thermostat,tv-app,tv-casting-app,water-leak-detector}[-asan][-boringssl][-chip-casting-simplified][-clang][-coverage][-disable-dnssd-tests][-dmalloc][-enable-dnssd-tests][-evse-test-event][-googletest][-ipv6only][-libfuzzer][-libnl][-mbedtls][-minmdns-verbose][-nfc-commission][-nlfaultinject][-no-ble][-no-interactive][-no-shell][-no-thread][-no-wifi][-no-wifipaf][-nodeps][-ossfuzz][-platform-mdns][-pw-fuzztest][-rpc][-same-event-loop][-terms-and-conditions][-test][-tsan][-ubsan][-unified][-webrtc][-with-ui] +linux-{arm64,x64}-{address-resolve-tool,air-purifier,air-quality-sensor,all-clusters,all-clusters-minimal,all-devices-app,bridge,camera,camera-controller,chip-cert,chip-tool,closure,contact-sensor,dishwasher,energy-gateway,energy-management,fabric-admin,fabric-bridge,fabric-sync,java-matter-controller,jf-admin-app,jf-control-app,kotlin-matter-controller,light,light-data-model-no-unique-id,lit-icd,lock,microwave-oven,minmdns,network-manager,ota-provider,ota-requestor,python-bindings,refrigerator,rpc-console,rvc,shell,simulated-app1,simulated-app2,terms-and-conditions,tests,thermostat,tv-app,tv-casting-app,water-leak-detector}[-asan][-boringssl][-chip-casting-simplified][-clang][-coverage][-disable-dnssd-tests][-dmalloc][-enable-dnssd-tests][-evse-test-event][-googletest][-ipv6only][-libfuzzer][-libnl][-mbedtls][-minmdns-verbose][-nfc-commission][-nlfaultinject][-no-ble][-no-interactive][-no-shell][-no-thread][-no-wifi][-no-wifipaf][-nodeps][-ossfuzz][-platform-mdns][-pw-fuzztest][-rpc][-same-event-loop][-terms-and-conditions][-test][-tsan][-ubsan][-unified][-webrtc][-with-ui] linux-x64-efr32-test-runner[-clang] imx-{all-clusters-app,all-clusters-minimal-app,chip-tool,lighting-app,ota-provider-app,thermostat}[-release][-trusty] infineon-psoc6-{all-clusters,all-clusters-minimal,light,lock}[-ota][-trustm][-updateimage] diff --git a/src/python_testing/TC_DeviceBasicComposition.py b/src/python_testing/TC_DeviceBasicComposition.py index 0742887c6fee25..62fd693ad80b87 100644 --- a/src/python_testing/TC_DeviceBasicComposition.py +++ b/src/python_testing/TC_DeviceBasicComposition.py @@ -156,6 +156,17 @@ # --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto # factory-reset: true # quiet: true +# run16: +# app: ${ALL_DEVICES_APP} +# app-args: --discriminator 1234 --KVS kvs1 +# script-args: > +# --storage-path admin_storage.json +# --manual-code 10054912339 +# --PICS src/app/tests/suites/certification/ci-pics-values +# --trace-to json:${TRACE_TEST_JSON}.json +# --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto +# factory-reset: true +# quiet: true # === END CI TEST ARGUMENTS === # Run 1: runs through all tests @@ -173,6 +184,7 @@ # Run 13: Tests against chip-rvc app # Run 14: Tests against network-management-app # Run 15: Tests against lighting-app-data-mode-no-unique-id +# Run 16: Tests against all-devices-app import logging from dataclasses import dataclass