Skip to content

Commit f0ec10b

Browse files
ReneJosefsenbzbarsky-applececille
authored
[Soil Measurement] Adds server implementation for soil measurement cluster (#38606)
* Enable soil measurement in all-clusters zap * Enabled soil measurement in all clusters * Added soil sensor to matter-devices * Initial server impl * Added all-clusters stub and reworked server impl * Added SetSoilMoistureMeasuredValue function * Added function to header file Added missing include and misspell * Removed server handling of measured value and added allcluster command to simulate measured value * Moved server to use global structs and enum * Remove redundant check * Marked SoilMoistureMeasuredValue as external * Added handling for cluster rev and soilMoistureMeasuredValue in server * Apply suggestions from code review Co-authored-by: Boris Zbarsky <[email protected]> * Reworked server implementation based on review feedback * Added ClusterRevision to list of external * Updated SetSoilMeasurementAccuracy comment * Add server build files * Remove unused includes and used metadata cluster revision * zap regen all * Addressed some review comments * Address additional review comments * Update src/app/clusters/soil-measurement-server/soil-measurement-server.cpp Co-authored-by: C Freeman <[email protected]> * Addressed review feedback * Reworked SetSimulatedSoilMoisture handling * Added check to only change and report on value changes and address review comments --------- Co-authored-by: Boris Zbarsky <[email protected]> Co-authored-by: C Freeman <[email protected]>
1 parent 03f2c7c commit f0ec10b

File tree

22 files changed

+548
-157
lines changed

22 files changed

+548
-157
lines changed

examples/all-clusters-app/all-clusters-common/all-clusters-app.matter

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6990,6 +6990,19 @@ cluster RadonConcentrationMeasurement = 1071 {
69906990
readonly attribute int16u clusterRevision = 65533;
69916991
}
69926992

6993+
/** This cluster provides an interface to soil measurement functionality, including configuration and provision of notifications of soil measurements. */
6994+
provisional cluster SoilMeasurement = 1072 {
6995+
revision 1;
6996+
6997+
readonly attribute MeasurementAccuracyStruct soilMoistureMeasurementLimits = 0;
6998+
readonly attribute nullable percent soilMoistureMeasuredValue = 1;
6999+
readonly attribute command_id generatedCommandList[] = 65528;
7000+
readonly attribute command_id acceptedCommandList[] = 65529;
7001+
readonly attribute attrib_id attributeList[] = 65531;
7002+
readonly attribute bitmap32 featureMap = 65532;
7003+
readonly attribute int16u clusterRevision = 65533;
7004+
}
7005+
69937006
/** This cluster provides an interface for managing low power mode on a device that supports the Wake On LAN protocol. */
69947007
cluster WakeOnLan = 1283 {
69957008
revision 1;
@@ -9708,6 +9721,16 @@ endpoint 1 {
97089721
ram attribute clusterRevision default = 3;
97099722
}
97109723

9724+
server cluster SoilMeasurement {
9725+
callback attribute soilMoistureMeasurementLimits;
9726+
callback attribute soilMoistureMeasuredValue;
9727+
callback attribute generatedCommandList;
9728+
callback attribute acceptedCommandList;
9729+
callback attribute attributeList;
9730+
ram attribute featureMap default = 0;
9731+
callback attribute clusterRevision;
9732+
}
9733+
97119734
server cluster WakeOnLan {
97129735
ram attribute MACAddress;
97139736
ram attribute featureMap default = 0;

examples/all-clusters-app/all-clusters-common/all-clusters-app.zap

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20776,6 +20776,129 @@
2077620776
}
2077720777
]
2077820778
},
20779+
{
20780+
"name": "Soil Measurement",
20781+
"code": 1072,
20782+
"mfgCode": null,
20783+
"define": "SOIL_MEASUREMENT_CLUSTER",
20784+
"side": "server",
20785+
"enabled": 1,
20786+
"apiMaturity": "provisional",
20787+
"attributes": [
20788+
{
20789+
"name": "SoilMoistureMeasurementLimits",
20790+
"code": 0,
20791+
"mfgCode": null,
20792+
"side": "server",
20793+
"type": "MeasurementAccuracyStruct",
20794+
"included": 1,
20795+
"storageOption": "External",
20796+
"singleton": 0,
20797+
"bounded": 0,
20798+
"defaultValue": "",
20799+
"reportable": 1,
20800+
"minInterval": 1,
20801+
"maxInterval": 65534,
20802+
"reportableChange": 0
20803+
},
20804+
{
20805+
"name": "SoilMoistureMeasuredValue",
20806+
"code": 1,
20807+
"mfgCode": null,
20808+
"side": "server",
20809+
"type": "percent",
20810+
"included": 1,
20811+
"storageOption": "RAM",
20812+
"singleton": 0,
20813+
"bounded": 0,
20814+
"defaultValue": null,
20815+
"reportable": 1,
20816+
"minInterval": 1,
20817+
"maxInterval": 65534,
20818+
"reportableChange": 0
20819+
},
20820+
{
20821+
"name": "GeneratedCommandList",
20822+
"code": 65528,
20823+
"mfgCode": null,
20824+
"side": "server",
20825+
"type": "array",
20826+
"included": 1,
20827+
"storageOption": "External",
20828+
"singleton": 0,
20829+
"bounded": 0,
20830+
"defaultValue": "",
20831+
"reportable": 1,
20832+
"minInterval": 1,
20833+
"maxInterval": 65534,
20834+
"reportableChange": 0
20835+
},
20836+
{
20837+
"name": "AcceptedCommandList",
20838+
"code": 65529,
20839+
"mfgCode": null,
20840+
"side": "server",
20841+
"type": "array",
20842+
"included": 1,
20843+
"storageOption": "External",
20844+
"singleton": 0,
20845+
"bounded": 0,
20846+
"defaultValue": "",
20847+
"reportable": 1,
20848+
"minInterval": 1,
20849+
"maxInterval": 65534,
20850+
"reportableChange": 0
20851+
},
20852+
{
20853+
"name": "AttributeList",
20854+
"code": 65531,
20855+
"mfgCode": null,
20856+
"side": "server",
20857+
"type": "array",
20858+
"included": 1,
20859+
"storageOption": "External",
20860+
"singleton": 0,
20861+
"bounded": 0,
20862+
"defaultValue": "",
20863+
"reportable": 1,
20864+
"minInterval": 1,
20865+
"maxInterval": 65534,
20866+
"reportableChange": 0
20867+
},
20868+
{
20869+
"name": "FeatureMap",
20870+
"code": 65532,
20871+
"mfgCode": null,
20872+
"side": "server",
20873+
"type": "bitmap32",
20874+
"included": 1,
20875+
"storageOption": "RAM",
20876+
"singleton": 0,
20877+
"bounded": 0,
20878+
"defaultValue": "0",
20879+
"reportable": 1,
20880+
"minInterval": 1,
20881+
"maxInterval": 65534,
20882+
"reportableChange": 0
20883+
},
20884+
{
20885+
"name": "ClusterRevision",
20886+
"code": 65533,
20887+
"mfgCode": null,
20888+
"side": "server",
20889+
"type": "int16u",
20890+
"included": 1,
20891+
"storageOption": "RAM",
20892+
"singleton": 0,
20893+
"bounded": 0,
20894+
"defaultValue": "1",
20895+
"reportable": 1,
20896+
"minInterval": 1,
20897+
"maxInterval": 65534,
20898+
"reportableChange": 0
20899+
}
20900+
]
20901+
},
2077920902
{
2078020903
"name": "Wake on LAN",
2078120904
"code": 1283,
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
*
3+
* Copyright (c) 2025 Project CHIP Authors
4+
* All rights reserved.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
#pragma once
20+
21+
#include <app/clusters/soil-measurement-server/soil-measurement-server.h>
22+
23+
namespace chip {
24+
namespace app {
25+
namespace Clusters {
26+
namespace SoilMeasurement {
27+
28+
Instance * GetInstance();
29+
30+
void Shutdown();
31+
32+
} // namespace SoilMeasurement
33+
} // namespace Clusters
34+
} // namespace app
35+
} // namespace chip
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
*
3+
* Copyright (c) 2025 Project CHIP Authors
4+
* All rights reserved.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
#include <soil-measurement-stub.h>
20+
21+
using namespace chip;
22+
using namespace chip::app;
23+
using namespace chip::app::Clusters;
24+
using namespace chip::app::Clusters::SoilMeasurement;
25+
26+
static const Globals::Structs::MeasurementAccuracyRangeStruct::Type kDefaultSoilMoistureMeasurementLimitsAccuracyRange[] = {
27+
{ .rangeMin = 0, .rangeMax = 100, .percentMax = MakeOptional(static_cast<chip::Percent100ths>(10)) }
28+
};
29+
30+
static const Globals::Structs::MeasurementAccuracyStruct::Type kDefaultSoilMoistureMeasurementLimits = {
31+
.measurementType = Globals::MeasurementTypeEnum::kSoilMoisture,
32+
.measured = true,
33+
.minMeasuredValue = 0,
34+
.maxMeasuredValue = 100,
35+
.accuracyRanges = DataModel::List<const Globals::Structs::MeasurementAccuracyRangeStruct::Type>(
36+
kDefaultSoilMoistureMeasurementLimitsAccuracyRange)
37+
};
38+
39+
namespace {
40+
static std::unique_ptr<Instance> gSoilMeasurementInstance;
41+
} // namespace
42+
43+
Instance * SoilMeasurement::GetInstance()
44+
{
45+
return gSoilMeasurementInstance.get();
46+
}
47+
48+
void SoilMeasurement::Shutdown()
49+
{
50+
VerifyOrDie(gSoilMeasurementInstance);
51+
gSoilMeasurementInstance->Shutdown();
52+
gSoilMeasurementInstance.reset(nullptr);
53+
}
54+
55+
void emberAfSoilMeasurementClusterInitCallback(EndpointId endpointId)
56+
{
57+
VerifyOrDie(endpointId == 1); // this cluster is only enabled for endpoint 1.
58+
VerifyOrDie(!gSoilMeasurementInstance);
59+
60+
gSoilMeasurementInstance = std::make_unique<Instance>(endpointId);
61+
if (gSoilMeasurementInstance)
62+
{
63+
gSoilMeasurementInstance->Init(kDefaultSoilMoistureMeasurementLimits);
64+
}
65+
}

examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include <oven-modes.h>
4040
#include <oven-operational-state-delegate.h>
4141
#include <rvc-modes.h>
42+
#include <soil-measurement-stub.h>
4243

4344
#include <memory>
4445
#include <string>
@@ -567,6 +568,29 @@ void AllClustersAppCommandHandler::HandleCommand(intptr_t context)
567568
ChipLogError(NotSpecified, "Failed to store configuration version:%d", configurationVersion);
568569
}
569570
}
571+
else if (name == "SetSimulatedSoilMoisture")
572+
{
573+
EndpointId endpoint = static_cast<EndpointId>(self->mJsonValue["EndpointId"].asUInt());
574+
Json::Value jsonSoilMoisture = self->mJsonValue["SoilMoistureValue"];
575+
DataModel::Nullable<Percent> soilMoistureMeasuredValue;
576+
577+
if (endpoint != 1)
578+
{
579+
ChipLogError(NotSpecified, "Invalid EndpointId to set Soil Moisture value.");
580+
return;
581+
}
582+
583+
if (jsonSoilMoisture.isNull())
584+
{
585+
soilMoistureMeasuredValue.SetNull();
586+
}
587+
else
588+
{
589+
soilMoistureMeasuredValue.SetNonNull(static_cast<uint8_t>(self->mJsonValue["SoilMoistureValue"].asUInt()));
590+
}
591+
592+
self->OnSoilMoistureChange(endpoint, soilMoistureMeasuredValue);
593+
}
570594
else
571595
{
572596
ChipLogError(NotSpecified, "Unhandled command '%s': this should never happen", name.c_str());
@@ -929,6 +953,27 @@ void AllClustersAppCommandHandler::OnAirQualityChange(uint32_t aNewValue)
929953
}
930954
}
931955

956+
void AllClustersAppCommandHandler::OnSoilMoistureChange(EndpointId endpointId, DataModel::Nullable<Percent> soilMoisture)
957+
{
958+
SoilMeasurement::Instance * soilMeasurementInstance = SoilMeasurement::GetInstance();
959+
960+
if (soilMoisture.IsNull())
961+
{
962+
ChipLogDetail(NotSpecified, "Set SoilMoisture value to null");
963+
}
964+
else if (soilMoisture.Value() > 100)
965+
{
966+
ChipLogDetail(NotSpecified, "Invalid SoilMoisture value");
967+
return;
968+
}
969+
else
970+
{
971+
ChipLogDetail(NotSpecified, "Set SoilMoisture value to %u", soilMoisture.Value());
972+
}
973+
974+
soilMeasurementInstance->SetSoilMeasuredValue(soilMoisture);
975+
}
976+
932977
void AllClustersAppCommandHandler::HandleSetOccupancyChange(EndpointId endpointId, uint8_t newOccupancyValue)
933978
{
934979
BitMask<chip::app::Clusters::OccupancySensing::OccupancyBitmap> currentOccupancy;

examples/all-clusters-app/linux/AllClustersCommandDelegate.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ class AllClustersAppCommandHandler
101101
*/
102102
void OnAirQualityChange(uint32_t aEnum);
103103

104+
/**
105+
* Should be called when it is necessary to change the measured moisture value.
106+
*/
107+
void OnSoilMoistureChange(chip::EndpointId endpointId, chip::app::DataModel::Nullable<chip::Percent> soilMoisture);
108+
104109
/**
105110
* Should be called when it is necessary to change the operational state as a manual operation.
106111
*/

examples/all-clusters-app/linux/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ source_set("chip-all-clusters-common") {
6262
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/rvc-operational-state-delegate-impl.cpp",
6363
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/smco-stub.cpp",
6464
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/software-diagnostics-stub.cpp",
65+
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/soil-measurement-stub.cpp",
6566
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-modes-manager.cpp",
6667
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-temperature-levels.cpp",
6768
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/tcc-mode.cpp",
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright (c) 2025 Project CHIP Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
group("soil-measurement-server") {
15+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Copyright (c) 2025 Project CHIP Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
# This is the equivalent to app_config_dependent_sources.gni
16+
TARGET_SOURCES(
17+
${APP_TARGET}
18+
PRIVATE
19+
"${CLUSTER_DIR}/soil-measurement-server.cpp"
20+
"${CLUSTER_DIR}/soil-measurement-server.h"
21+
)

0 commit comments

Comments
 (0)