diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml
index 44b5e0957d8800..79306dbbddf281 100644
--- a/.github/workflows/tests.yaml
+++ b/.github/workflows/tests.yaml
@@ -97,6 +97,7 @@ jobs:
src/app/zap-templates/zcl/data-model/chip/global-bitmaps.xml \
src/app/zap-templates/zcl/data-model/chip/global-enums.xml \
src/app/zap-templates/zcl/data-model/chip/global-structs.xml \
+ src/app/zap-templates/zcl/data-model/chip/meter-identification-cluster.xml \
src/app/zap-templates/zcl/data-model/chip/push-av-stream-transport-cluster.xml \
src/app/zap-templates/zcl/data-model/chip/semantic-tag-namespace-enums.xml \
src/app/zap-templates/zcl/data-model/chip/access-control-definitions.xml \
diff --git a/data_model/master/clusters/BasicInformationCluster.xml b/data_model/master/clusters/BasicInformationCluster.xml
index 0bad75b2fedf35..0ebd340479459e 100644
--- a/data_model/master/clusters/BasicInformationCluster.xml
+++ b/data_model/master/clusters/BasicInformationCluster.xml
@@ -63,7 +63,7 @@ Davis, CA 95616, USA
-
+
@@ -351,11 +351,6 @@ Davis, CA 95616, USA
-
-
-
-
-
diff --git a/data_model/master/clusters/Descriptor-Cluster.xml b/data_model/master/clusters/Descriptor-Cluster.xml
index 25597accd43bc2..1c31324a844c2e 100644
--- a/data_model/master/clusters/Descriptor-Cluster.xml
+++ b/data_model/master/clusters/Descriptor-Cluster.xml
@@ -57,11 +57,10 @@ Davis, CA 95616, USA
:xrefstyle: basic
-->
-
+
-
@@ -124,13 +123,5 @@ Davis, CA 95616, USA
-
-
-
-
-
-
-
-
diff --git a/data_model/master/clusters/GeneralCommissioningCluster.xml b/data_model/master/clusters/GeneralCommissioningCluster.xml
index ada2cbb5ea8041..dfdb10b00db38c 100644
--- a/data_model/master/clusters/GeneralCommissioningCluster.xml
+++ b/data_model/master/clusters/GeneralCommissioningCluster.xml
@@ -223,19 +223,6 @@ Davis, CA 95616, USA
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/data_model/master/clusters/Humidistat.xml b/data_model/master/clusters/Humidistat.xml
new file mode 100644
index 00000000000000..8d2da1c717c542
--- /dev/null
+++ b/data_model/master/clusters/Humidistat.xml
@@ -0,0 +1,292 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data_model/master/clusters/MeterIdentification.xml b/data_model/master/clusters/MeterIdentification.xml
index ebb0bcf7e7865a..82dd43b2b73185 100644
--- a/data_model/master/clusters/MeterIdentification.xml
+++ b/data_model/master/clusters/MeterIdentification.xml
@@ -82,6 +82,29 @@ Davis, CA 95616, USA
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data_model/master/clusters/Mode_RVCClean.xml b/data_model/master/clusters/Mode_RVCClean.xml
index c8d399b425e7bf..dfd76377271c19 100644
--- a/data_model/master/clusters/Mode_RVCClean.xml
+++ b/data_model/master/clusters/Mode_RVCClean.xml
@@ -57,12 +57,11 @@ Davis, CA 95616, USA
:xrefstyle: basic
-->
-
+
-
@@ -72,9 +71,6 @@ Davis, CA 95616, USA
-
-
-
@@ -91,7 +87,6 @@ Davis, CA 95616, USA
-
diff --git a/data_model/master/clusters/Mode_RVCRun.xml b/data_model/master/clusters/Mode_RVCRun.xml
index dfd0bef99edec2..dee25b2e942304 100644
--- a/data_model/master/clusters/Mode_RVCRun.xml
+++ b/data_model/master/clusters/Mode_RVCRun.xml
@@ -71,9 +71,6 @@ Davis, CA 95616, USA
-
-
-
diff --git a/data_model/master/clusters/NetworkIdentityManagement.xml b/data_model/master/clusters/NetworkIdentityManagement.xml
new file mode 100644
index 00000000000000..ca6b029c351e06
--- /dev/null
+++ b/data_model/master/clusters/NetworkIdentityManagement.xml
@@ -0,0 +1,198 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data_model/master/clusters/OperationalState_RVC.xml b/data_model/master/clusters/OperationalState_RVC.xml
index 2e334eeffec1e7..4e2ee237382b7e 100644
--- a/data_model/master/clusters/OperationalState_RVC.xml
+++ b/data_model/master/clusters/OperationalState_RVC.xml
@@ -57,11 +57,10 @@ Davis, CA 95616, USA
:xrefstyle: basic
-->
-
+
-
@@ -105,27 +104,6 @@ Davis, CA 95616, USA
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
-
@@ -149,18 +127,6 @@ Davis, CA 95616, USA
-
- -
-
-
- -
-
-
- -
-
-
- -
-
-
diff --git a/data_model/master/clusters/PushAVStreamTransport.xml b/data_model/master/clusters/PushAVStreamTransport.xml
index 464ecebd5564c3..01ccb5632975b5 100644
--- a/data_model/master/clusters/PushAVStreamTransport.xml
+++ b/data_model/master/clusters/PushAVStreamTransport.xml
@@ -315,7 +315,7 @@ Davis, CA 95616, USA
-
+
diff --git a/data_model/master/clusters/Thermostat.xml b/data_model/master/clusters/Thermostat.xml
index 805f49ed8e405f..4e9e6eecf8ee82 100644
--- a/data_model/master/clusters/Thermostat.xml
+++ b/data_model/master/clusters/Thermostat.xml
@@ -67,7 +67,7 @@ Davis, CA 95616, USA
-
+
@@ -116,14 +116,6 @@ Davis, CA 95616, USA
-
-
-
-
-
-
-
-
@@ -521,17 +513,6 @@ Davis, CA 95616, USA
-
-
-
-
-
-
-
-
-
-
-
@@ -688,20 +669,6 @@ Davis, CA 95616, USA
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1272,33 +1239,6 @@ Davis, CA 95616, USA
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1418,218 +1358,5 @@ Davis, CA 95616, USA
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/data_model/master/clusters/bridge-clusters-BridgedDeviceBasicInformationCluster.xml b/data_model/master/clusters/bridge-clusters-BridgedDeviceBasicInformationCluster.xml
index f4e55c7653caf4..83fabc2c793786 100644
--- a/data_model/master/clusters/bridge-clusters-BridgedDeviceBasicInformationCluster.xml
+++ b/data_model/master/clusters/bridge-clusters-BridgedDeviceBasicInformationCluster.xml
@@ -63,7 +63,7 @@ Davis, CA 95616, USA
-
+
@@ -243,11 +243,6 @@ Davis, CA 95616, USA
-
-
-
-
-
diff --git a/data_model/master/clusters/cluster_ids.json b/data_model/master/clusters/cluster_ids.json
index dd91f798138588..2c7c56ce8df31c 100644
--- a/data_model/master/clusters/cluster_ids.json
+++ b/data_model/master/clusters/cluster_ids.json
@@ -84,6 +84,7 @@
"513": "Thermostat",
"514": "Fan Control",
"516": "Thermostat User Interface Configuration",
+ "517": "Humidistat",
"768": "Color Control",
"1024": "Illuminance Measurement",
"1026": "Temperature Measurement",
@@ -102,6 +103,7 @@
"1070": "Total Volatile Organic Compounds Concentration Measurement",
"1071": "Radon Concentration Measurement",
"1072": "Soil Measurement",
+ "1104": "Network Identity Management",
"1105": "Wi-Fi Network Management",
"1106": "Thread Border Router Management",
"1107": "Thread Network Directory",
@@ -134,4 +136,4 @@
"2049": "TLS Certificate Management",
"2050": "TLS Client Management",
"2822": "Meter Identification"
-}
+}
\ No newline at end of file
diff --git a/data_model/master/device_types/HumidifierDehumidifier.xml b/data_model/master/device_types/HumidifierDehumidifier.xml
new file mode 100644
index 00000000000000..e882e809653421
--- /dev/null
+++ b/data_model/master/device_types/HumidifierDehumidifier.xml
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data_model/master/device_types/NetworkInfraManager.xml b/data_model/master/device_types/NetworkInfraManager.xml
index 9f97ab3307c1bc..df60a5cfb049cd 100644
--- a/data_model/master/device_types/NetworkInfraManager.xml
+++ b/data_model/master/device_types/NetworkInfraManager.xml
@@ -64,9 +64,6 @@ Davis, CA 95616, USA
-
-
-
diff --git a/data_model/master/device_types/Thermostat.xml b/data_model/master/device_types/Thermostat.xml
index 32a5df5d6790d9..47a9e183c5e818 100644
--- a/data_model/master/device_types/Thermostat.xml
+++ b/data_model/master/device_types/Thermostat.xml
@@ -74,9 +74,6 @@ Davis, CA 95616, USA
-
-
-
diff --git a/data_model/master/device_types/ThreadBorderRouter.xml b/data_model/master/device_types/ThreadBorderRouter.xml
index e1f6326d6bf9aa..a794613da72f5b 100644
--- a/data_model/master/device_types/ThreadBorderRouter.xml
+++ b/data_model/master/device_types/ThreadBorderRouter.xml
@@ -63,9 +63,6 @@ Davis, CA 95616, USA
-
-
-
diff --git a/data_model/master/device_types/WaterHeater.xml b/data_model/master/device_types/WaterHeater.xml
index 014d5d934bc3c5..470c172fcd1dbe 100644
--- a/data_model/master/device_types/WaterHeater.xml
+++ b/data_model/master/device_types/WaterHeater.xml
@@ -69,9 +69,6 @@ Davis, CA 95616, USA
-
-
-
diff --git a/data_model/master/namespaces/Namespace-Common-Area.xml b/data_model/master/namespaces/Namespace-Common-Area.xml
new file mode 100644
index 00000000000000..60dab7a443c4ca
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-Common-Area.xml
@@ -0,0 +1,186 @@
+
+
+
+
+
+
+
+
+
+
+
+ Also known as Restroom
+
+
+
+
+ A small room typically used for storage
+
+
+
+
+
+
+ A small room for storing clothing, linens, and other items.
+
+
+
+
+
+
+
+ A small, comfortable room for individual activities such as work or hobbies
+
+
+
+
+
+
+
+ A bathroom directly accessible from a bedroom
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Also known as Guest Restroom
+
+
+
+ Also known as Guest Bedroom
+
+
+
+
+ A cozy room containing a fireplace or other point heat source
+
+
+
+
+
+
+
+
+
+
+
+ A space used to remove soiled garments prior to entering the domicile proper
+
+
+
+
+
+
+
+ AKA a larder, a place where food is stored
+
+
+
+
+
+
+ A room centered around a pool/billiards table
+
+
+
+
+
+
+
+
+
+
+ A utility space for cleaning dishes and laundry
+
+
+
+
+
+
+
+ An informal space meant to be 'cozy', 'snug', relaxed, meant to share with family or friends
+
+
+
+
+
+
+
+
+
+
+
+
+ The innermost area of a large home
+
+
+
+ A room dedicated to a toilet; a water closet / WC
+
+
+
diff --git a/data_model/master/namespaces/Namespace-Common-Closure.xml b/data_model/master/namespaces/Namespace-Common-Closure.xml
new file mode 100644
index 00000000000000..6cc806308e300b
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-Common-Closure.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+ Move toward open position
+
+
+ Move toward closed position
+
+
+ Stop any movement
+
+
+
diff --git a/data_model/master/namespaces/Namespace-Common-CompassDirection.xml b/data_model/master/namespaces/Namespace-Common-CompassDirection.xml
new file mode 100644
index 00000000000000..7366ae2da3cc7d
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-Common-CompassDirection.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data_model/master/namespaces/Namespace-Common-CompassLocation.xml b/data_model/master/namespaces/Namespace-Common-CompassLocation.xml
new file mode 100644
index 00000000000000..8623edf70ea09a
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-Common-CompassLocation.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data_model/master/namespaces/Namespace-Common-Direction.xml b/data_model/master/namespaces/Namespace-Common-Direction.xml
new file mode 100644
index 00000000000000..8d888f32a1faaf
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-Common-Direction.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data_model/master/namespaces/Namespace-Common-Landmark.xml b/data_model/master/namespaces/Namespace-Common-Landmark.xml
new file mode 100644
index 00000000000000..5df0a292c1f66a
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-Common-Landmark.xml
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ An indoor furnishing for pets to rest or sleep inside
+
+
+
+
+
+ An area where a showerhead dispenses water for people to shower
+
+
+
+
+
+
+
+
+
+
+
+ A type of refrigerator that is shelved to hold wine bottles and (typically) display them through a glass front
+
+
+
diff --git a/data_model/master/namespaces/Namespace-Common-Level.xml b/data_model/master/namespaces/Namespace-Common-Level.xml
new file mode 100644
index 00000000000000..fb709116e8a788
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-Common-Level.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
diff --git a/data_model/master/namespaces/Namespace-Common-Location.xml b/data_model/master/namespaces/Namespace-Common-Location.xml
new file mode 100644
index 00000000000000..fce22bcb5126d8
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-Common-Location.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+ Element is indoors or related to indoor equipment/conditions (e.g. the "indoor" temperature).
+
+
+ Element is outdoors or related to outdoor equipment/conditions (e.g. the "outdoor" temperature).
+
+
+ Element is located inside the equipment (e.g. a sensor "inside" a cabinet).
+
+
+ Element is located outside the equipment (e.g. a sensor "outside" a cabinet)
+
+
+
diff --git a/data_model/master/namespaces/Namespace-Common-Number.xml b/data_model/master/namespaces/Namespace-Common-Number.xml
new file mode 100644
index 00000000000000..53917dcb9cc42f
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-Common-Number.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data_model/master/namespaces/Namespace-Common-RelativePosition.xml b/data_model/master/namespaces/Namespace-Common-RelativePosition.xml
new file mode 100644
index 00000000000000..239229197b71a0
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-Common-RelativePosition.xml
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+ Area in proximity to the point of reference
+
+
+ The area surrounding the point the reference
+
+
+
+
+
+
+
diff --git a/data_model/master/namespaces/Namespace-ElectricalMeasurement.xml b/data_model/master/namespaces/Namespace-ElectricalMeasurement.xml
new file mode 100644
index 00000000000000..84f8a192d8b485
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-ElectricalMeasurement.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+ Indicates values measured for a DC load
+
+
+ Indicates values measured for a single-phase AC load, or values measured for the collective load on a polyphase AC power supply
+
+
+ Indicates values measured for an AC load on phase 1 of a polyphase power supply
+
+
+ Indicates values measured for an AC load on phase 2 of a polyphase power supply
+
+
+ Indicates values measured for an AC load on phase 3 of a polyphase power supply
+
+
+
diff --git a/data_model/master/namespaces/Namespace-EnergyTariff-Chronology.xml b/data_model/master/namespaces/Namespace-EnergyTariff-Chronology.xml
new file mode 100644
index 00000000000000..294d04b15d437c
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-EnergyTariff-Chronology.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+ Represents the current energy tariff
+
+
+ Represents the previous energy tariff
+
+
+ Represents the upcoming energy tariff
+
+
+
diff --git a/data_model/master/namespaces/Namespace-EnergyTariff-Commodity.xml b/data_model/master/namespaces/Namespace-EnergyTariff-Commodity.xml
new file mode 100644
index 00000000000000..c311ddfbc5fe53
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-EnergyTariff-Commodity.xml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
diff --git a/data_model/master/namespaces/Namespace-EnergyTariff-Flow.xml b/data_model/master/namespaces/Namespace-EnergyTariff-Flow.xml
new file mode 100644
index 00000000000000..019eef1729445f
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-EnergyTariff-Flow.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
diff --git a/data_model/master/namespaces/Namespace-GenericClosure-AwningClosure.xml b/data_model/master/namespaces/Namespace-GenericClosure-AwningClosure.xml
new file mode 100644
index 00000000000000..9b68c39d175272
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-GenericClosure-AwningClosure.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+ For any Awning not listed below
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data_model/master/namespaces/Namespace-GenericClosure-BarrierClosure.xml b/data_model/master/namespaces/Namespace-GenericClosure-BarrierClosure.xml
new file mode 100644
index 00000000000000..bd70df69d0073a
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-GenericClosure-BarrierClosure.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+ For any Barrier not listed below
+
+
+
+
+
+
+
+
+
diff --git a/data_model/master/namespaces/Namespace-GenericClosure-BlindClosure.xml b/data_model/master/namespaces/Namespace-GenericClosure-BlindClosure.xml
new file mode 100644
index 00000000000000..6935e60722f399
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-GenericClosure-BlindClosure.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+ For any Blind not listed below
+
+
+
+
+
+
+
+
diff --git a/data_model/master/namespaces/Namespace-GenericClosure-CabinetClosure.xml b/data_model/master/namespaces/Namespace-GenericClosure-CabinetClosure.xml
new file mode 100644
index 00000000000000..714e9758a95b0c
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-GenericClosure-CabinetClosure.xml
@@ -0,0 +1,103 @@
+
+
+
+
+
+ For any Cabinet that cannot be further determined in application or not listed below
+
+
+ For any drawer/pull-out that cannot be further determined in application or listed below
+
+
+
+
+
+
+
+ For any flap that cannot be further determined in application or listed below
+
+
+
+
+
+
+
+ For any door that cannot be further determined in application or listed below
+
+
+ For any side hinged door that cannot be further determined in application or listed below
+
+
+
+
+
+
+
+ For any Bi-Fold and/or Sliding Door that cannot be further determined in application or listed below
+
+
+
+
+
+
+
+
+
+
diff --git a/data_model/master/namespaces/Namespace-GenericClosure-CurtainClosure.xml b/data_model/master/namespaces/Namespace-GenericClosure-CurtainClosure.xml
new file mode 100644
index 00000000000000..47f9da447e3a7d
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-GenericClosure-CurtainClosure.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
+ For any Curtain not listed below
+
+
+
+
+
+
+
diff --git a/data_model/master/namespaces/Namespace-GenericClosure-DoorClosure.xml b/data_model/master/namespaces/Namespace-GenericClosure-DoorClosure.xml
new file mode 100644
index 00000000000000..8a9cc08fd8acda
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-GenericClosure-DoorClosure.xml
@@ -0,0 +1,79 @@
+
+
+
+
+
+ For any Door not listed below
+
+
+ Not a door lock
+
+
+
+
+
+
+
+ Metal rolling curtain in front of stores
+
+
+
+
+
diff --git a/data_model/master/namespaces/Namespace-GenericClosure-GarageDoorClosure.xml b/data_model/master/namespaces/Namespace-GenericClosure-GarageDoorClosure.xml
new file mode 100644
index 00000000000000..890a14370fdc58
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-GenericClosure-GarageDoorClosure.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+ For any Garage Door not listed below
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data_model/master/namespaces/Namespace-GenericClosure-GateClosure.xml b/data_model/master/namespaces/Namespace-GenericClosure-GateClosure.xml
new file mode 100644
index 00000000000000..30c8a9215acf1e
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-GenericClosure-GateClosure.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+ For any Gate not listed below
+
+
+
+
+
+
+
+
+
diff --git a/data_model/master/namespaces/Namespace-GenericClosure-PergolaClosure.xml b/data_model/master/namespaces/Namespace-GenericClosure-PergolaClosure.xml
new file mode 100644
index 00000000000000..de01f1ff3ccb93
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-GenericClosure-PergolaClosure.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+ For any Pergola not listed below
+
+
+
+
+
+
diff --git a/data_model/master/namespaces/Namespace-GenericClosure-ScreenClosure.xml b/data_model/master/namespaces/Namespace-GenericClosure-ScreenClosure.xml
new file mode 100644
index 00000000000000..3866851143820b
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-GenericClosure-ScreenClosure.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+ For any Screen not listed below
+
+
+
+
+
diff --git a/data_model/master/namespaces/Namespace-GenericClosure-ShadeClosure.xml b/data_model/master/namespaces/Namespace-GenericClosure-ShadeClosure.xml
new file mode 100644
index 00000000000000..f1dd18b58ffc1b
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-GenericClosure-ShadeClosure.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+ For any Shade not listed below
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data_model/master/namespaces/Namespace-GenericClosure-ShutterClosure.xml b/data_model/master/namespaces/Namespace-GenericClosure-ShutterClosure.xml
new file mode 100644
index 00000000000000..9b1a44b50df951
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-GenericClosure-ShutterClosure.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+ For any Shutter not listed below
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data_model/master/namespaces/Namespace-GenericClosure-WindowClosure.xml b/data_model/master/namespaces/Namespace-GenericClosure-WindowClosure.xml
new file mode 100644
index 00000000000000..c8cde8e947f131
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-GenericClosure-WindowClosure.xml
@@ -0,0 +1,99 @@
+
+
+
+
+
+ For any window not listed below
+
+
+
+ The window sash is hinged on the left-hand side of the frame
+
+
+ The window sash is hinged in the middle of the frame
+
+
+ The window sash is hinged on the right-hand side of the frame
+
+
+ The window sash is hinged at the top of the frame
+
+
+ The window sash is hinged at the bottom of the frame
+
+
+ The window sash is parallel hinged - used for parallel opening windows
+
+
+ The window consists of a left- and right-hinged sash
+
+
+ The window is a sliding element
+
+
+
+
+
+
+
+
+
+
+
diff --git a/data_model/master/namespaces/Namespace-Laundry.xml b/data_model/master/namespaces/Namespace-Laundry.xml
new file mode 100644
index 00000000000000..5922e66b6b1d4d
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-Laundry.xml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/data_model/master/namespaces/Namespace-PowerSource.xml b/data_model/master/namespaces/Namespace-PowerSource.xml
new file mode 100644
index 00000000000000..37523a1a8b0bda
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-PowerSource.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+ The Power Source cluster is related to power provided from an unknown source
+
+
+ The Power Source cluster is related to power provided from the electrical grid
+
+
+ The Power Source cluster is related to power provided from a solar panel array
+
+
+ The Power Source cluster is related to power provided from a battery
+
+
+ The Power Source cluster is related to power provided from an electric vehicle
+
+
+
diff --git a/data_model/master/namespaces/Namespace-Refrigerator.xml b/data_model/master/namespaces/Namespace-Refrigerator.xml
new file mode 100644
index 00000000000000..d823a0016cc4e7
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-Refrigerator.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
diff --git a/data_model/master/namespaces/Namespace-RoomAirConditioner.xml b/data_model/master/namespaces/Namespace-RoomAirConditioner.xml
new file mode 100644
index 00000000000000..9697586acf19f5
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-RoomAirConditioner.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
diff --git a/data_model/master/namespaces/Namespace-Switches.xml b/data_model/master/namespaces/Namespace-Switches.xml
new file mode 100644
index 00000000000000..2f1fffa1f78e7f
--- /dev/null
+++ b/data_model/master/namespaces/Namespace-Switches.xml
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+ e.g. dim up (light)
+
+
+ e.g. dim down (light)
+
+
+ e.g. select next scene
+
+
+ e.g. select previous scene
+
+
+
+ Textual description provided in Label field
+
+
+
diff --git a/docs/ids_and_codes/zap_clusters.md b/docs/ids_and_codes/zap_clusters.md
index f1e018fcf471af..59d35e7177c948 100644
--- a/docs/ids_and_codes/zap_clusters.md
+++ b/docs/ids_and_codes/zap_clusters.md
@@ -140,6 +140,7 @@ Generally regenerate using one of:
| 1872 | 0x750 | EcosystemInformation |
| 1873 | 0x751 | CommissionerControl |
| 2049 | 0x801 | TlsCertificateManagement |
+| 2822 | 0xB06 | MeterIdentification |
| 4294048773 | 0xFFF1FC05 | UnitTesting |
| 4294048774 | 0xFFF1FC06 | FaultInjection |
| 4294048800 | 0xFFF1FC20 | SampleMei |
diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter
index 8790aa3df77f70..de29c572147a67 100644
--- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter
+++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter
@@ -7036,6 +7036,45 @@ provisional cluster Chime = 1366 {
command PlayChimeSound(): DefaultSuccess = 0;
}
+/** */
+cluster MeterIdentification = 2822 {
+ revision 1;
+
+ enum MeterTypeEnum : enum8 {
+ kUtility = 0;
+ kPrivate = 1;
+ kGeneric = 2;
+ }
+
+ enum PowerThresholdSourceEnum : enum8 {
+ kContract = 0;
+ kRegulator = 1;
+ kEquipment = 2;
+ }
+
+ bitmap Feature : bitmap32 {
+ kPowerThreshold = 0x1;
+ }
+
+ struct PowerThresholdStruct {
+ optional int64s powerThreshold = 0;
+ optional int64s apparentPowerThreshold = 1;
+ nullable PowerThresholdSourceEnum powerThresholdSource = 2;
+ }
+
+ readonly attribute nullable MeterTypeEnum meterType = 0;
+ readonly attribute nullable char_string<64> pointOfDelivery = 1;
+ readonly attribute nullable char_string<64> meterSerialNumber = 2;
+ readonly attribute optional nullable char_string<64> protocolVersion = 3;
+ readonly attribute optional nullable PowerThresholdStruct powerThreshold = 4;
+ readonly attribute command_id generatedCommandList[] = 65528;
+ readonly attribute command_id acceptedCommandList[] = 65529;
+ readonly attribute event_id eventList[] = 65530;
+ readonly attribute attrib_id attributeList[] = 65531;
+ readonly attribute bitmap32 featureMap = 65532;
+ readonly attribute int16u clusterRevision = 65533;
+}
+
/** The Test Cluster is meant to validate the generated code */
internal cluster UnitTesting = 4294048773 {
revision 1; // NOTE: Default/not specifically set
@@ -9326,6 +9365,19 @@ endpoint 1 {
handle command PlayChimeSound;
}
+ server cluster MeterIdentification {
+ ram attribute meterType;
+ ram attribute pointOfDelivery;
+ ram attribute meterSerialNumber;
+ ram attribute protocolVersion;
+ callback attribute powerThreshold;
+ callback attribute generatedCommandList;
+ callback attribute acceptedCommandList;
+ callback attribute attributeList;
+ ram attribute featureMap default = 0;
+ ram attribute clusterRevision default = 1;
+ }
+
server cluster UnitTesting {
emits event TestEvent;
emits event TestFabricScopedEvent;
diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap b/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap
index f6d5b34380fb84..4224871841bb47 100644
--- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap
+++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap
@@ -20987,7 +20987,7 @@
"storageOption": "External",
"singleton": 0,
"bounded": 0,
- "defaultValue": "",
+ "defaultValue": null,
"reportable": 1,
"minInterval": 1,
"maxInterval": 65534,
@@ -21003,7 +21003,7 @@
"storageOption": "External",
"singleton": 0,
"bounded": 0,
- "defaultValue": "",
+ "defaultValue": null,
"reportable": 1,
"minInterval": 1,
"maxInterval": 65534,
@@ -21019,7 +21019,7 @@
"storageOption": "External",
"singleton": 0,
"bounded": 0,
- "defaultValue": "",
+ "defaultValue": null,
"reportable": 1,
"minInterval": 1,
"maxInterval": 65534,
@@ -21035,7 +21035,7 @@
"storageOption": "External",
"singleton": 0,
"bounded": 0,
- "defaultValue": "",
+ "defaultValue": null,
"reportable": 1,
"minInterval": 1,
"maxInterval": 65534,
@@ -21051,7 +21051,7 @@
"storageOption": "External",
"singleton": 0,
"bounded": 0,
- "defaultValue": "",
+ "defaultValue": null,
"reportable": 1,
"minInterval": 1,
"maxInterval": 65534,
@@ -21067,12 +21067,182 @@
"storageOption": "External",
"singleton": 0,
"bounded": 0,
+ "defaultValue": null,
+ "reportable": 1,
+ "minInterval": 1,
+ "maxInterval": 65534,
+ "reportableChange": 0
+ },
+ {
+ "name": "FeatureMap",
+ "code": 65532,
+ "mfgCode": null,
+ "side": "server",
+ "type": "bitmap32",
+ "included": 1,
+ "storageOption": "RAM",
+ "singleton": 0,
+ "bounded": 0,
+ "defaultValue": "0",
+ "reportable": 1,
+ "minInterval": 1,
+ "maxInterval": 65534,
+ "reportableChange": 0
+ },
+ {
+ "name": "ClusterRevision",
+ "code": 65533,
+ "mfgCode": null,
+ "side": "server",
+ "type": "int16u",
+ "included": 1,
+ "storageOption": "RAM",
+ "singleton": 0,
+ "bounded": 0,
+ "defaultValue": "1",
+ "reportable": 1,
+ "minInterval": 1,
+ "maxInterval": 65534,
+ "reportableChange": 0
+ }
+ ]
+ },
+ {
+ "name": "Meter Identification",
+ "code": 2822,
+ "mfgCode": null,
+ "define": "METER_IDENTIFICATION_CLUSTER",
+ "side": "server",
+ "enabled": 1,
+ "attributes": [
+ {
+ "name": "MeterType",
+ "code": 0,
+ "mfgCode": null,
+ "side": "server",
+ "type": "MeterTypeEnum",
+ "included": 1,
+ "storageOption": "RAM",
+ "singleton": 0,
+ "bounded": 0,
+ "defaultValue": "",
+ "reportable": 1,
+ "minInterval": 1,
+ "maxInterval": 65534,
+ "reportableChange": 0
+ },
+ {
+ "name": "PointOfDelivery",
+ "code": 1,
+ "mfgCode": null,
+ "side": "server",
+ "type": "char_string",
+ "included": 1,
+ "storageOption": "RAM",
+ "singleton": 0,
+ "bounded": 0,
+ "defaultValue": "",
+ "reportable": 1,
+ "minInterval": 1,
+ "maxInterval": 65534,
+ "reportableChange": 0
+ },
+ {
+ "name": "MeterSerialNumber",
+ "code": 2,
+ "mfgCode": null,
+ "side": "server",
+ "type": "char_string",
+ "included": 1,
+ "storageOption": "RAM",
+ "singleton": 0,
+ "bounded": 0,
+ "defaultValue": "",
+ "reportable": 1,
+ "minInterval": 1,
+ "maxInterval": 65534,
+ "reportableChange": 0
+ },
+ {
+ "name": "ProtocolVersion",
+ "code": 3,
+ "mfgCode": null,
+ "side": "server",
+ "type": "char_string",
+ "included": 1,
+ "storageOption": "RAM",
+ "singleton": 0,
+ "bounded": 0,
+ "defaultValue": "",
+ "reportable": 1,
+ "minInterval": 1,
+ "maxInterval": 65534,
+ "reportableChange": 0
+ },
+ {
+ "name": "PowerThreshold",
+ "code": 4,
+ "mfgCode": null,
+ "side": "server",
+ "type": "PowerThresholdStruct",
+ "included": 1,
+ "storageOption": "External",
+ "singleton": 0,
+ "bounded": 0,
"defaultValue": "",
"reportable": 1,
"minInterval": 1,
"maxInterval": 65534,
"reportableChange": 0
},
+ {
+ "name": "GeneratedCommandList",
+ "code": 65528,
+ "mfgCode": null,
+ "side": "server",
+ "type": "array",
+ "included": 1,
+ "storageOption": "External",
+ "singleton": 0,
+ "bounded": 0,
+ "defaultValue": null,
+ "reportable": 1,
+ "minInterval": 1,
+ "maxInterval": 65534,
+ "reportableChange": 0
+ },
+ {
+ "name": "AcceptedCommandList",
+ "code": 65529,
+ "mfgCode": null,
+ "side": "server",
+ "type": "array",
+ "included": 1,
+ "storageOption": "External",
+ "singleton": 0,
+ "bounded": 0,
+ "defaultValue": null,
+ "reportable": 1,
+ "minInterval": 1,
+ "maxInterval": 65534,
+ "reportableChange": 0
+ },
+ {
+ "name": "AttributeList",
+ "code": 65531,
+ "mfgCode": null,
+ "side": "server",
+ "type": "array",
+ "included": 1,
+ "storageOption": "External",
+ "singleton": 0,
+ "bounded": 0,
+ "defaultValue": null,
+ "reportable": 1,
+ "minInterval": 1,
+ "maxInterval": 65534,
+ "reportableChange": 0
+ },
{
"name": "FeatureMap",
"code": 65532,
diff --git a/examples/all-clusters-app/all-clusters-common/include/meter-identification-delegate.h b/examples/all-clusters-app/all-clusters-common/include/meter-identification-delegate.h
new file mode 100644
index 00000000000000..28e79851ed6d37
--- /dev/null
+++ b/examples/all-clusters-app/all-clusters-common/include/meter-identification-delegate.h
@@ -0,0 +1,94 @@
+/*
+ *
+ * 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
+#include
+#include
+#include
+
+namespace chip {
+namespace app {
+namespace Clusters {
+namespace MeterIdentification {
+
+class MeterIdentificationDelegate : public MeterIdentification::Delegate
+{
+ // Attribute storage
+ DataModel::Nullable mMeterType;
+ DataModel::Nullable mPointOfDelivery;
+ DataModel::Nullable mMeterSerialNumber;
+ DataModel::Nullable mProtocolVersion;
+ DataModel::Nullable mPowerThreshold;
+
+private:
+
+ static bool NullableCharSpanCompare(const DataModel::Nullable & a, const DataModel::Nullable & b);
+ static CHIP_ERROR NullableCharSpanCopy(const DataModel::Nullable & src, DataModel::Nullable & dst);
+ static CHIP_ERROR JsonToPowerThreshold(const Json::Value & root, Structs::PowerThresholdStruct::Type & value);
+
+public:
+
+ virtual ~MeterIdentificationDelegate();
+
+ void Init();
+
+ CHIP_ERROR LoadJson(const Json::Value & root);
+
+ // Attribute Accessors
+
+ DataModel::Nullable GetMeterType() override { return mMeterType; }
+ DataModel::Nullable GetPointOfDelivery() override { return mPointOfDelivery; }
+ DataModel::Nullable GetMeterSerialNumber() override { return mMeterSerialNumber; }
+ DataModel::Nullable GetProtocolVersion() override { return mProtocolVersion; }
+ DataModel::Nullable GetPowerThreshold() override { return mPowerThreshold; }
+
+ // Internal Application API to set attribute values
+ CHIP_ERROR SetMeterType(const DataModel::Nullable & value);
+ CHIP_ERROR SetPointOfDelivery(const DataModel::Nullable & value);
+ CHIP_ERROR SetMeterSerialNumber(const DataModel::Nullable & value);
+ CHIP_ERROR SetProtocolVersion(const DataModel::Nullable & value);
+ CHIP_ERROR SetPowerThreshold(const DataModel::Nullable & value);
+};
+
+class MeterIdentificationInstance : public Instance
+{
+ MeterIdentificationDelegate * mDelegate;
+
+public:
+
+ MeterIdentificationInstance(EndpointId aEndpointId, MeterIdentificationDelegate & aDelegate, const Feature & aFeature) :
+ MeterIdentification::Instance(aEndpointId, aDelegate, aFeature), mDelegate(&aDelegate) {}
+
+ // Delete copy constructor and assignment operator.
+ MeterIdentificationInstance(const MeterIdentificationInstance &) = delete;
+ MeterIdentificationInstance(const MeterIdentificationInstance &&) = delete;
+ MeterIdentificationInstance & operator=(const MeterIdentificationInstance &) = delete;
+
+ CHIP_ERROR Init();
+ void Shutdown();
+
+ MeterIdentificationDelegate * GetDelegate() { return mDelegate; };
+};
+
+MeterIdentificationDelegate * GetDelegate();
+
+} // namespace MeterIdentification
+} // namespace Clusters
+} // namespace app
+} // namespace chip
diff --git a/examples/all-clusters-app/all-clusters-common/src/MeterIdentificationEventTriggers.cpp b/examples/all-clusters-app/all-clusters-common/src/MeterIdentificationEventTriggers.cpp
new file mode 100644
index 00000000000000..f8917e5bb0d1d8
--- /dev/null
+++ b/examples/all-clusters-app/all-clusters-common/src/MeterIdentificationEventTriggers.cpp
@@ -0,0 +1,228 @@
+/*
+ *
+ * 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
+
+using namespace chip;
+using namespace chip::app;
+using namespace chip::app::Clusters;
+using namespace chip::app::Clusters::MeterIdentification;
+
+namespace {
+
+class OldMeterIdentificationAttributes
+{
+ DataModel::Nullable mMeterType;
+ DataModel::Nullable mPointOfDelivery;
+ DataModel::Nullable mMeterSerialNumber;
+ DataModel::Nullable mProtocolVersion;
+ DataModel::Nullable mPowerThreshold;
+
+private:
+
+ DataModel::Nullable mDelegate;
+
+ static void SetCharSpan(DataModel::Nullable & charSpan, const DataModel::Nullable && value)
+ {
+ if (!value.IsNull())
+ {
+ const size_t len = value.Value().size();
+ char * str = (char *) chip::Platform::MemoryAlloc(len + 1);
+ strncpy(str, value.Value().data(), len);
+ str[len] = 0;
+ charSpan = DataModel::MakeNullable(CharSpan::fromCharString(str));
+ }
+ }
+
+ static void CleanCharSpan(DataModel::Nullable & charSpan)
+ {
+ if (!charSpan.IsNull())
+ {
+ chip::Platform::MemoryFree((void *) charSpan.Value().data());
+ charSpan.SetNull();
+ }
+ }
+
+ static std::string IncrementString(const std::string &&string)
+ {
+ constexpr char minC = ' ', maxC = '~';
+
+ std::string ret{minC};
+
+ auto rit = string.rbegin();
+ while (rit != string.rend())
+ {
+ if (maxC == *rit)
+ {
+ ++rit;
+ if (rit == string.rend())
+ {
+ ret = string + ret;
+ break;
+ }
+ }
+ else
+ {
+ ret = string;
+ ++(*(ret.rbegin() + std::distance(string.rbegin(), rit)));
+ break;
+ }
+ }
+
+ return ret;
+ }
+
+ void SaveAttributes()
+ {
+ mDelegate.SetNonNull(GetDelegate());
+ VerifyOrDieWithMsg(mDelegate.Value() != nullptr, AppServer, "Meter Identification Delegate is null");
+ mMeterType = mDelegate.Value()->GetMeterType();
+ SetCharSpan(mPointOfDelivery, mDelegate.Value()->GetPointOfDelivery());
+ SetCharSpan(mMeterSerialNumber, mDelegate.Value()->GetMeterSerialNumber());
+ SetCharSpan(mProtocolVersion, mDelegate.Value()->GetProtocolVersion());
+ const auto && powerThreshold = mDelegate.Value()->GetPowerThreshold();
+ if (!powerThreshold.IsNull())
+ {
+ mPowerThreshold.SetNonNull(powerThreshold.Value());
+ }
+ }
+
+ void ClearAttributes()
+ {
+ CleanCharSpan(mPointOfDelivery);
+ CleanCharSpan(mMeterSerialNumber);
+ CleanCharSpan(mProtocolVersion);
+ mMeterType.SetNull();
+ mPowerThreshold.SetNull();
+ mDelegate.SetNull();
+ }
+
+ void RestoreAttributes() const
+ {
+ if (!mDelegate.IsNull())
+ {
+ mDelegate.Value()->SetMeterType(mMeterType);
+ mDelegate.Value()->SetPointOfDelivery(mPointOfDelivery);
+ mDelegate.Value()->SetMeterSerialNumber(mMeterSerialNumber);
+ mDelegate.Value()->SetProtocolVersion(mProtocolVersion);
+ mDelegate.Value()->SetPowerThreshold(mPowerThreshold);
+ }
+ }
+
+ void IncreaseAttributes()
+ {
+ if (mDelegate.Value()->GetMeterType().IsNull())
+ {
+ mDelegate.Value()->SetMeterType(DataModel::MakeNullable(static_cast(0)));
+ }
+ else
+ {
+ mDelegate.Value()->SetMeterType(DataModel::MakeNullable(static_cast(1 +
+ static_cast::type>(mDelegate.Value()->GetMeterType().Value()))));
+ }
+
+ if (mDelegate.Value()->GetPointOfDelivery().IsNull())
+ {
+ mDelegate.Value()->SetPointOfDelivery(DataModel::MakeNullable(CharSpan::fromCharString("")));
+ }
+ else
+ {
+ mDelegate.Value()->SetPointOfDelivery(DataModel::MakeNullable(CharSpan::fromCharString(IncrementString(
+ mDelegate.Value()->GetPointOfDelivery().Value().data()).c_str())));
+ }
+
+ if (mDelegate.Value()->GetMeterSerialNumber().IsNull())
+ {
+ mDelegate.Value()->SetMeterSerialNumber(DataModel::MakeNullable(CharSpan::fromCharString("")));
+ }
+ else
+ {
+ mDelegate.Value()->SetMeterSerialNumber(DataModel::MakeNullable(CharSpan::fromCharString(IncrementString(
+ mDelegate.Value()->GetMeterSerialNumber().Value().data()).c_str())));
+ }
+
+ if (mDelegate.Value()->GetProtocolVersion().IsNull())
+ {
+ mDelegate.Value()->SetProtocolVersion(DataModel::MakeNullable(CharSpan::fromCharString("")));
+ }
+ else
+ {
+ mDelegate.Value()->SetProtocolVersion(DataModel::MakeNullable(CharSpan::fromCharString(IncrementString(
+ mDelegate.Value()->GetProtocolVersion().Value().data()).c_str())));
+ }
+
+ if (mDelegate.Value()->GetPowerThreshold().IsNull())
+ {
+ mDelegate.Value()->SetPowerThreshold(DataModel::MakeNullable((Structs::PowerThresholdStruct::Type){Optional(0),
+ Optional(0), static_cast(0)}));
+ }
+ else
+ {
+ auto powerThreshold = mDelegate.Value()->GetPowerThreshold();
+ ++powerThreshold.Value().powerThreshold.Value();
+ ++powerThreshold.Value().apparentPowerThreshold.Value();
+ powerThreshold.Value().powerThresholdSource.Value() = static_cast(1 +
+ static_cast::type>(powerThreshold.Value().powerThresholdSource.Value()));
+ mDelegate.Value()->SetPowerThreshold(std::move(powerThreshold));
+ }
+ }
+
+public:
+
+ void Update()
+ {
+ if (mDelegate.IsNull())
+ {
+ SaveAttributes();
+ }
+
+ IncreaseAttributes();
+ }
+
+ void Clear()
+ {
+ RestoreAttributes();
+ ClearAttributes();
+ }
+};
+
+OldMeterIdentificationAttributes mOldMeterIdentificationAttributes;
+} // namespace
+
+bool HandleMeterIdentificationTestEventTrigger(uint64_t eventTrigger)
+{
+ MeterIdentificationTrigger trigger = static_cast(eventTrigger);
+
+ switch (trigger)
+ {
+ case MeterIdentificationTrigger::kAttributesValueUpdate:
+ ChipLogProgress(Support, "[MTRID-Test-Event] => Attributes value update");
+ mOldMeterIdentificationAttributes.Update();
+ break;
+ case MeterIdentificationTrigger::kAttributesValueUpdateClear:
+ ChipLogProgress(Support, "[MTRID-Test-Event] => Attributes value clear");
+ mOldMeterIdentificationAttributes.Clear();
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
diff --git a/examples/all-clusters-app/all-clusters-common/src/meter-identification-delegate.cpp b/examples/all-clusters-app/all-clusters-common/src/meter-identification-delegate.cpp
new file mode 100644
index 00000000000000..9a2c61ade503b4
--- /dev/null
+++ b/examples/all-clusters-app/all-clusters-common/src/meter-identification-delegate.cpp
@@ -0,0 +1,410 @@
+/*
+ *
+ * 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.
+ */
+
+#include "meter-identification-delegate.h"
+#include
+
+#include
+
+using namespace chip;
+using namespace chip::app;
+using namespace chip::app::DataModel;
+using namespace chip::app::Clusters;
+using namespace chip::app::Clusters::MeterIdentification;
+using namespace chip::app::Clusters::MeterIdentification::Attributes;
+
+using chip::app::Clusters::MeterIdentification::MeterTypeEnum;
+using Feature = chip::app::Clusters::MeterIdentification::Feature;
+
+namespace chip::app::Clusters::MeterIdentification {
+ void RestartServer(uint32_t features);
+}
+
+CHIP_ERROR MeterIdentificationInstance::Init()
+{
+ return Instance::Init();
+}
+
+void MeterIdentificationInstance::Shutdown()
+{
+ Instance::Shutdown();
+}
+
+// --------------- Internal Attribute Set APIs
+
+chip::app::Clusters::MeterIdentification::MeterIdentificationDelegate::~MeterIdentificationDelegate()
+{
+ if (!mPointOfDelivery.IsNull())
+ {
+ chip::Platform::MemoryFree((void *) mPointOfDelivery.Value().data());
+ mPointOfDelivery.SetNull();
+ }
+
+ if (!mMeterSerialNumber.IsNull())
+ {
+ chip::Platform::MemoryFree((void *) mMeterSerialNumber.Value().data());
+ mMeterSerialNumber.SetNull();
+ }
+
+ if (!mProtocolVersion.IsNull())
+ {
+ chip::Platform::MemoryFree((void *) mProtocolVersion.Value().data());
+ mProtocolVersion.SetNull();
+ }
+}
+
+void MeterIdentificationDelegate::Init()
+{
+ SetMeterType(std::nullopt);
+ SetPointOfDelivery(std::nullopt);
+ SetMeterSerialNumber(std::nullopt);
+ SetProtocolVersion(std::nullopt);
+ SetPowerThreshold(std::nullopt);
+}
+
+CHIP_ERROR MeterIdentificationDelegate::LoadJson(const Json::Value & root)
+{
+ CHIP_ERROR ret = CHIP_NO_ERROR;
+ Json::Value value;
+
+ if (root.isMember("Features"))
+ {
+ value = root.get("Features", Json::Value());
+ if (value.isUInt())
+ {
+ if(1 >= value.asUInt())
+ {
+ RestartServer(value.asUInt());
+ return ret;
+ }
+ }
+ else
+ {
+ ret = CHIP_ERROR_DECODE_FAILED;
+ }
+ }
+
+ if (root.isMember("MeterType"))
+ {
+ value = root.get("MeterType", Json::Value());
+ if(value.isUInt())
+ {
+ ret = CHIP_NO_ERROR == ret ? SetMeterType(MakeNullable(static_cast(value.asUInt()))) : ret;
+ }
+ else
+ {
+ ret = CHIP_ERROR_DECODE_FAILED;
+ }
+ }
+
+ if (root.isMember("PointOfDelivery"))
+ {
+ value = root.get("PointOfDelivery", Json::Value());
+ if(value.isString())
+ {
+ ret = CHIP_NO_ERROR == ret ? SetPointOfDelivery(CharSpan::fromCharString(value.asCString())) : ret;
+ }
+ else
+ {
+ ret = CHIP_ERROR_DECODE_FAILED;
+ }
+ }
+
+ if (root.isMember("MeterSerialNumber"))
+ {
+ value = root.get("MeterSerialNumber", Json::Value());
+ if(value.isString())
+ {
+ ret = CHIP_NO_ERROR == ret ? SetMeterSerialNumber(CharSpan::fromCharString(value.asCString())) : ret;
+ }
+ else
+ {
+ ret = CHIP_ERROR_DECODE_FAILED;
+ }
+ }
+
+ if (root.isMember("ProtocolVersion"))
+ {
+ value = root.get("ProtocolVersion", Json::Value());
+ if(value.isString())
+ {
+ ret = CHIP_NO_ERROR == ret ? SetProtocolVersion(CharSpan::fromCharString(value.asCString())) : ret;
+ }
+ else
+ {
+ ret = CHIP_ERROR_DECODE_FAILED;
+ }
+ }
+
+ if (root.isMember("PowerThreshold"))
+ {
+ value = root.get("PowerThreshold", Json::Value());
+ if (!value.empty() && value.isObject())
+ {
+ Structs::PowerThresholdStruct::Type powerThreshopld;
+ const CHIP_ERROR error = JsonToPowerThreshold(value, powerThreshopld);
+ if (CHIP_NO_ERROR == error)
+ {
+ ret = CHIP_NO_ERROR == ret ? SetPowerThreshold(MakeNullable(powerThreshopld)) : ret;
+ }
+ else
+ {
+ ret = error;
+ }
+ }
+ else
+ {
+ ret = CHIP_ERROR_DECODE_FAILED;
+ }
+ }
+
+ return ret;
+}
+
+CHIP_ERROR MeterIdentificationDelegate::SetMeterType(const DataModel::Nullable & newValue)
+{
+ if (newValue.IsNull())
+ {
+ if (mMeterType.IsNull())
+ {
+ return CHIP_NO_ERROR;
+ }
+
+ mMeterType.SetNull();
+ }
+ else
+ {
+ if (!mMeterType.IsNull() && mMeterType.Value() == newValue.Value())
+ {
+ return CHIP_NO_ERROR;
+ }
+
+ if(static_cast::type>(MeterTypeEnum::kUnknownEnumValue) <=
+ static_cast::type>(newValue.Value()))
+ {
+ return CHIP_ERROR_INVALID_INTEGER_VALUE;
+ }
+
+ mMeterType.SetNonNull(newValue.Value());
+ }
+
+ MatterReportingAttributeChangeCallback(mEndpointId, MeterIdentification::Id, MeterType::Id);
+ return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR MeterIdentificationDelegate::SetPointOfDelivery(const DataModel::Nullable & newValue)
+{
+ if (NullableCharSpanCompare(newValue, mPointOfDelivery))
+ {
+ return CHIP_NO_ERROR;
+ }
+
+ const CHIP_ERROR ret = NullableCharSpanCopy(newValue, mPointOfDelivery);
+ if (CHIP_NO_ERROR == ret)
+ {
+ MatterReportingAttributeChangeCallback(mEndpointId, MeterIdentification::Id, PointOfDelivery::Id);
+ }
+ return ret;
+}
+
+CHIP_ERROR MeterIdentificationDelegate::SetMeterSerialNumber(const DataModel::Nullable & newValue)
+{
+ if (NullableCharSpanCompare(newValue, mMeterSerialNumber))
+ {
+ return CHIP_NO_ERROR;
+ }
+
+ const CHIP_ERROR ret = NullableCharSpanCopy(newValue, mMeterSerialNumber);
+ if (CHIP_NO_ERROR == ret)
+ {
+ MatterReportingAttributeChangeCallback(mEndpointId, MeterIdentification::Id, MeterSerialNumber::Id);
+ }
+ return ret;
+}
+
+CHIP_ERROR MeterIdentificationDelegate::SetProtocolVersion(const DataModel::Nullable & newValue)
+{
+ if (NullableCharSpanCompare(newValue, mProtocolVersion))
+ {
+ return CHIP_NO_ERROR;
+ }
+
+ const CHIP_ERROR ret = NullableCharSpanCopy(newValue, mProtocolVersion);
+ if (CHIP_NO_ERROR == ret)
+ {
+ MatterReportingAttributeChangeCallback(mEndpointId, MeterIdentification::Id, ProtocolVersion::Id);
+ }
+ return ret;
+}
+
+CHIP_ERROR MeterIdentificationDelegate::SetPowerThreshold(const DataModel::Nullable & newValue)
+{
+ if (newValue.IsNull())
+ {
+ if (mPowerThreshold.IsNull())
+ {
+ return CHIP_NO_ERROR;
+ }
+
+ mPowerThreshold.SetNull();
+ }
+ else
+ {
+ if (!mPowerThreshold.IsNull() && (newValue.Value().powerThreshold == mPowerThreshold.Value().powerThreshold &&
+ newValue.Value().apparentPowerThreshold == mPowerThreshold.Value().apparentPowerThreshold &&
+ newValue.Value().powerThresholdSource == mPowerThreshold.Value().powerThresholdSource))
+ {
+ return CHIP_NO_ERROR;
+ }
+
+ if ((newValue.Value().powerThreshold.HasValue() && INT64_MIN == newValue.Value().powerThreshold.Value()) ||
+ (newValue.Value().apparentPowerThreshold.HasValue() && INT64_MIN == newValue.Value().apparentPowerThreshold.Value()) ||
+ (!newValue.Value().powerThresholdSource.IsNull() && static_cast::type>(
+ PowerThresholdSourceEnum::kUnknownEnumValue) <= static_cast::type>(
+ newValue.Value().powerThresholdSource.Value())))
+ {
+ return CHIP_ERROR_DECODE_FAILED;
+ }
+
+ mPowerThreshold.SetNonNull(newValue.Value());
+ }
+
+ MatterReportingAttributeChangeCallback(mEndpointId, MeterIdentification::Id, PowerThreshold::Id);
+ return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR MeterIdentificationDelegate::JsonToPowerThreshold(const Json::Value & root, Structs::PowerThresholdStruct::Type & value)
+{
+ Json::Value t;
+ if (root.isMember("PowerThreshold"))
+ {
+ t = root.get("PowerThreshold", Json::Value());
+ if (t.empty() || !t.isInt64())
+ {
+ return CHIP_ERROR_DECODE_FAILED;
+ }
+
+ value.powerThreshold = Optional(t.asUInt());
+ }
+
+ if (root.isMember("ApparentPowerThreshold"))
+ {
+ t = root.get("ApparentPowerThreshold", Json::Value());
+ if (t.empty() || !t.isInt64())
+ {
+ return CHIP_ERROR_DECODE_FAILED;
+ }
+
+ value.apparentPowerThreshold = Optional(t.asUInt());
+ }
+
+ if (root.isMember("PowerThresholdSource"))
+ {
+ t = root.get("PowerThresholdSource", Json::Value());
+ if (t.empty() || !t.isUInt())
+ {
+ return CHIP_ERROR_DECODE_FAILED;
+ }
+
+ value.powerThresholdSource.SetNonNull(static_cast(t.asUInt()));
+ }
+
+ return CHIP_NO_ERROR;
+}
+
+bool MeterIdentificationDelegate::NullableCharSpanCompare(const DataModel::Nullable & a, const DataModel::Nullable & b)
+{
+ if (a.IsNull() && b.IsNull())
+ {
+ return true;
+ }
+
+ if (!a.IsNull() && !b.IsNull())
+ {
+ return a.Value().data_equal(b.Value());
+ }
+
+ return false;
+}
+
+CHIP_ERROR MeterIdentificationDelegate::NullableCharSpanCopy(const DataModel::Nullable & src, DataModel::Nullable & dst)
+{
+ const size_t len = src.IsNull() ? 0 : src.Value().size();
+ if (64 < len)
+ {
+ return CHIP_ERROR_INVALID_STRING_LENGTH;
+ }
+
+ if (!dst.IsNull())
+ {
+ chip::Platform::MemoryFree((void *) dst.Value().data());
+ dst.SetNull();
+ }
+
+ if (!src.IsNull())
+ {
+ char * str = (char *) chip::Platform::MemoryAlloc(len + 1);
+ strncpy(str, src.Value().data(), len);
+ str[len] = 0;
+ dst = MakeNullable(CharSpan::fromCharString(str));
+ }
+
+ return CHIP_NO_ERROR;
+}
+
+static std::unique_ptr gMIDelegate;
+static std::unique_ptr gMIInstance;
+static BitMask gMIFeature = BitMask(Feature::kPowerThreshold);
+
+void emberAfMeterIdentificationClusterInitCallback(chip::EndpointId endpointId)
+{
+ ChipLogProgress(Zcl, "emberAfMeterIdentificationClusterInitCallback %d", (int)endpointId);
+
+ VerifyOrDie(endpointId == 1); // this cluster is only enabled for endpoint 1.
+ VerifyOrDie(!gMIInstance);
+
+ gMIDelegate = std::make_unique();
+ if (gMIDelegate)
+ {
+ gMIDelegate->Init();
+
+ gMIInstance = std::make_unique(endpointId, *gMIDelegate, gMIFeature);
+
+ gMIInstance->Init();
+ }
+}
+
+MeterIdentificationDelegate * chip::app::Clusters::MeterIdentification::GetDelegate()
+{
+ return &(*gMIDelegate);
+}
+
+namespace chip::app::Clusters::MeterIdentification {
+
+void RestartServer(uint32_t features)
+{
+ gMIFeature = static_cast>(features);
+
+ VerifyOrDie(gMIInstance);
+ gMIInstance.reset();
+
+ gMIInstance = std::make_unique(1, *gMIDelegate, gMIFeature);
+ gMIInstance->Init();
+}
+
+}
\ No newline at end of file
diff --git a/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp b/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp
index e0278f30cc4b72..0a07850819042c 100644
--- a/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp
+++ b/examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp
@@ -30,6 +30,7 @@
#include
#include "ButtonEventsSimulator.h"
+#include "meter-identification-delegate.h"
#include
#include
#include
@@ -491,6 +492,10 @@ void AllClustersAppCommandHandler::HandleCommand(intptr_t context)
{
HandleSimulateLatchPosition(self->mJsonValue);
}
+ else if (name == "MeterIdentification")
+ {
+ self->OnMeterIdentificationHandler(self->mJsonValue);
+ }
else if (name == "SimulateSwitchIdle")
{
HandleSimulateSwitchIdle(self->mJsonValue);
@@ -859,6 +864,16 @@ void AllClustersAppCommandHandler::OnOvenOperationalStateChange(std::string devi
}
}
+void AllClustersAppCommandHandler::OnMeterIdentificationHandler(const Json::Value & param)
+{
+ MeterIdentification::MeterIdentificationDelegate * delegate = MeterIdentification::GetDelegate();
+ const CHIP_ERROR error = delegate->LoadJson(param);
+ if (CHIP_NO_ERROR != error)
+ {
+ ChipLogError(NotSpecified, "Error: %s in the JSON: %s", ErrorStr(error), param.toStyledString().c_str());
+ }
+}
+
void AllClustersAppCommandHandler::OnAirQualityChange(uint32_t aNewValue)
{
AirQuality::Instance * airQualityInstance = AirQuality::GetInstance();
diff --git a/examples/all-clusters-app/linux/AllClustersCommandDelegate.h b/examples/all-clusters-app/linux/AllClustersCommandDelegate.h
index b0d1f2e99750ca..3e1f1d32df21d4 100644
--- a/examples/all-clusters-app/linux/AllClustersCommandDelegate.h
+++ b/examples/all-clusters-app/linux/AllClustersCommandDelegate.h
@@ -116,6 +116,11 @@ class AllClustersAppCommandHandler
*/
void OnOvenOperationalStateChange(std::string device, std::string operation, Json::Value param);
+ /**
+ * Should be called when it is necessary to change one or some attributes.
+ */
+ void OnMeterIdentificationHandler(const Json::Value & param);
+
/**
* Should be called when it is necessary to change the Occupancy attribute.
*/
diff --git a/examples/all-clusters-app/linux/BUILD.gn b/examples/all-clusters-app/linux/BUILD.gn
index b044dc4cdd91a8..f3ae62654a4d9b 100644
--- a/examples/all-clusters-app/linux/BUILD.gn
+++ b/examples/all-clusters-app/linux/BUILD.gn
@@ -47,6 +47,7 @@ source_set("chip-all-clusters-common") {
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/laundry-dryer-controls-delegate-impl.cpp",
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/laundry-washer-controls-delegate-impl.cpp",
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/laundry-washer-mode.cpp",
+ "${chip_root}/examples/all-clusters-app/all-clusters-common/src/meter-identification-delegate.cpp",
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/microwave-oven-mode.cpp",
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/occupancy-sensing-stub.cpp",
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/operational-state-delegate-impl.cpp",
@@ -62,6 +63,7 @@ source_set("chip-all-clusters-common") {
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-temperature-levels.cpp",
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/tcc-mode.cpp",
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/wifi-diagnostics-stub.cpp",
+ "${chip_root}/examples/all-clusters-app/all-clusters-common/src/MeterIdentificationEventTriggers.cpp",
"${chip_root}/examples/all-clusters-app/linux/diagnostic-logs-provider-delegate-impl.cpp",
"${chip_root}/examples/energy-management-app/energy-management-common/common/src/EnergyTimeUtils.cpp",
"${chip_root}/examples/energy-management-app/energy-management-common/device-energy-management/src/DeviceEnergyManagementDelegateImpl.cpp",
diff --git a/examples/all-clusters-app/linux/args.gni b/examples/all-clusters-app/linux/args.gni
index 068178d83b988c..3f3907015ca2c4 100644
--- a/examples/all-clusters-app/linux/args.gni
+++ b/examples/all-clusters-app/linux/args.gni
@@ -29,6 +29,7 @@ matter_log_json_payload_decode_full = true
matter_log_json_payload_hex = true
chip_enable_smoke_co_trigger = true
chip_enable_boolean_state_configuration_trigger = true
+chip_enable_meter_identification_trigger = true
chip_enable_water_heater_management_trigger = true
chip_enable_software_diagnostics_trigger = true
chip_enable_wifi_diagnostics_trigger = true
diff --git a/examples/platform/linux/AppMain.cpp b/examples/platform/linux/AppMain.cpp
index ca3e47717a1a3e..dd1360e2d0cbcd 100644
--- a/examples/platform/linux/AppMain.cpp
+++ b/examples/platform/linux/AppMain.cpp
@@ -91,6 +91,9 @@
#if CHIP_DEVICE_CONFIG_ENABLE_ENERGY_REPORTING_TRIGGER
#include
#endif
+#if CHIP_DEVICE_CONFIG_ENABLE_METER_IDENTIFICATION_TRIGGER
+#include
+#endif
#if CHIP_DEVICE_CONFIG_ENABLE_WATER_HEATER_MANAGEMENT_TRIGGER
#include
#endif
@@ -634,6 +637,10 @@ void ChipLinuxAppMainLoop(AppMainLoopImplementation * impl)
static EnergyReportingTestEventTriggerHandler sEnergyReportingTestEventTriggerHandler;
sTestEventTriggerDelegate.AddHandler(&sEnergyReportingTestEventTriggerHandler);
#endif
+#if CHIP_DEVICE_CONFIG_ENABLE_METER_IDENTIFICATION_TRIGGER
+ static MeterIdentificationTestEventTriggerHandler sMeterIdentificationTestEventTriggerHandler;
+ sTestEventTriggerDelegate.AddHandler(&sMeterIdentificationTestEventTriggerHandler);
+#endif
#if CHIP_DEVICE_CONFIG_ENABLE_WATER_HEATER_MANAGEMENT_TRIGGER
static WaterHeaterManagementTestEventTriggerHandler sWaterHeaterManagementTestEventTriggerHandler;
sTestEventTriggerDelegate.AddHandler(&sWaterHeaterManagementTestEventTriggerHandler);
diff --git a/examples/platform/linux/BUILD.gn b/examples/platform/linux/BUILD.gn
index a31a72ed834e15..37d1f6c2d5184a 100644
--- a/examples/platform/linux/BUILD.gn
+++ b/examples/platform/linux/BUILD.gn
@@ -31,6 +31,7 @@ declare_args() {
chip_enable_boolean_state_configuration_trigger = false
chip_enable_energy_evse_trigger = false
chip_enable_energy_reporting_trigger = false
+ chip_enable_meter_identification_trigger = false
chip_enable_water_heater_management_trigger = false
chip_enable_device_energy_management_trigger = false
}
@@ -69,6 +70,10 @@ source_set("energy-reporting-test-event-trigger") {
sources = [ "${chip_root}/src/app/clusters/electrical-energy-measurement-server/EnergyReportingTestEventTriggerHandler.h" ]
}
+source_set("meter-identification-test-event-trigger") {
+ sources = [ "${chip_root}/src/app/clusters/meter-identification-server/MeterIdentificationTestEventTriggerHandler.h" ]
+}
+
source_set("water-heater-management-test-event-trigger") {
sources = [ "${chip_root}/src/app/clusters/water-heater-management-server/WaterHeaterManagementTestEventTriggerHandler.h" ]
}
@@ -104,6 +109,7 @@ source_set("app-main") {
":energy-reporting-test-event-trigger",
":smco-test-event-trigger",
":software-diagnostics-test-event-trigger",
+ ":meter-identification-test-event-trigger",
":water-heater-management-test-event-trigger",
":wifi-diagnostics-test-event-trigger",
"${chip_root}/src/data-model-providers/codegen:instance-header",
@@ -156,6 +162,7 @@ source_set("app-main") {
"CHIP_DEVICE_CONFIG_ENABLE_BOOLEAN_STATE_CONFIGURATION_TRIGGER=${chip_enable_boolean_state_configuration_trigger}",
"CHIP_DEVICE_CONFIG_ENABLE_ENERGY_EVSE_TRIGGER=${chip_enable_energy_evse_trigger}",
"CHIP_DEVICE_CONFIG_ENABLE_ENERGY_REPORTING_TRIGGER=${chip_enable_energy_reporting_trigger}",
+ "CHIP_DEVICE_CONFIG_ENABLE_METER_IDENTIFICATION_TRIGGER=${chip_enable_meter_identification_trigger}",
"CHIP_DEVICE_CONFIG_ENABLE_WATER_HEATER_MANAGEMENT_TRIGGER=${chip_enable_water_heater_management_trigger}",
"CHIP_DEVICE_CONFIG_ENABLE_WIFI_DIAGNOSTIC_TRIGGER=${chip_enable_wifi_diagnostics_trigger}",
"CHIP_DEVICE_CONFIG_ENABLE_DEVICE_ENERGY_MANAGEMENT_TRIGGER=${chip_enable_device_energy_management_trigger}",
diff --git a/scripts/build/build/targets.py b/scripts/build/build/targets.py
index 75b58c2247739b..573f95dbe8272c 100755
--- a/scripts/build/build/targets.py
+++ b/scripts/build/build/targets.py
@@ -194,6 +194,7 @@ def BuildHostTarget():
target.AppendModifier('rpc', enable_rpcs=True)
target.AppendModifier('with-ui', imgui_ui=True)
target.AppendModifier('evse-test-event', enable_test_event_triggers=['EVSE']).OnlyIfRe('-energy-management')
+ target.AppendModifier('mtrid-test-event', enable_test_event_triggers=['MTRID']).OnlyIfRe('-meter-identification')
target.AppendModifier('enable-dnssd-tests', enable_dnssd_tests=True).OnlyIfRe('-tests')
target.AppendModifier('disable-dnssd-tests', enable_dnssd_tests=False).OnlyIfRe('-tests')
target.AppendModifier('chip-casting-simplified', chip_casting_simplified=True).OnlyIfRe('-tv-casting-app')
diff --git a/scripts/build/builders/host.py b/scripts/build/builders/host.py
index e39396e721b051..f876561110dcd6 100644
--- a/scripts/build/builders/host.py
+++ b/scripts/build/builders/host.py
@@ -470,6 +470,8 @@ def __init__(self, root, runner, app: HostApp, board=HostBoard.NATIVE,
if enable_test_event_triggers is not None:
if 'EVSE' in enable_test_event_triggers:
self.extra_gn_options.append('chip_enable_energy_evse_trigger=true')
+ if 'MTRID' in enable_test_event_triggers:
+ self.extra_gn_options.append('chip_enable_meter_identification_trigger=true')
if enable_dnssd_tests is not None:
if enable_dnssd_tests:
diff --git a/scripts/py_matter_idl/matter_idl/tests/outputs/large_all_clusters_app/cpp-app/cluster-init-callback.cpp b/scripts/py_matter_idl/matter_idl/tests/outputs/large_all_clusters_app/cpp-app/cluster-init-callback.cpp
index 650d59d3dddf05..9af8a67d4df7af 100644
--- a/scripts/py_matter_idl/matter_idl/tests/outputs/large_all_clusters_app/cpp-app/cluster-init-callback.cpp
+++ b/scripts/py_matter_idl/matter_idl/tests/outputs/large_all_clusters_app/cpp-app/cluster-init-callback.cpp
@@ -115,6 +115,9 @@ void emberAfClusterInitCallback(EndpointId endpoint, ClusterId clusterId)
case app::Clusters::MediaPlayback::Id:
emberAfMediaPlaybackClusterInitCallback(endpoint);
break;
+ case app::Clusters::MeterIdentification::Id:
+ emberAfMeterIdentificationClusterInitCallback(endpoint);
+ break;
case app::Clusters::ModeSelect::Id:
emberAfModeSelectClusterInitCallback(endpoint);
break;
diff --git a/scripts/rules.matterlint b/scripts/rules.matterlint
index 5e1f8959119d57..57fb02228dd195 100644
--- a/scripts/rules.matterlint
+++ b/scripts/rules.matterlint
@@ -35,6 +35,7 @@ load "../src/app/zap-templates/zcl/data-model/chip/diagnostic-logs-cluster.xml";
load "../src/app/zap-templates/zcl/data-model/chip/dishwasher-alarm-cluster.xml";
load "../src/app/zap-templates/zcl/data-model/chip/dishwasher-mode-cluster.xml";
load "../src/app/zap-templates/zcl/data-model/chip/measurement-and-sensing.xml";
+load "../src/app/zap-templates/zcl/data-model/chip/meter-identification-cluster.xml";
load "../src/app/zap-templates/zcl/data-model/chip/microwave-oven-mode-cluster.xml";
load "../src/app/zap-templates/zcl/data-model/chip/microwave-oven-control-cluster.xml";
load "../src/app/zap-templates/zcl/data-model/chip/door-lock-cluster.xml";
diff --git a/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/gen_config.h b/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/gen_config.h
index 7ad28113645da5..27bb257614af02 100644
--- a/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/gen_config.h
+++ b/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/gen_config.h
@@ -149,6 +149,7 @@
#define MATTER_DM_ECOSYSTEM_INFORMATION_CLUSTER_SERVER_ENDPOINT_COUNT (0)
#define MATTER_DM_COMMISSIONER_CONTROL_CLUSTER_SERVER_ENDPOINT_COUNT (0)
#define MATTER_DM_TLS_CERTIFICATE_MANAGEMENT_CLUSTER_SERVER_ENDPOINT_COUNT (0)
+#define MATTER_DM_METER_IDENTIFICATION_CLUSTER_SERVER_ENDPOINT_COUNT (0)
#define MATTER_DM_UNIT_TESTING_CLUSTER_SERVER_ENDPOINT_COUNT (1)
#define MATTER_DM_FAULT_INJECTION_CLUSTER_SERVER_ENDPOINT_COUNT (1)
#define MATTER_DM_SAMPLE_MEI_CLUSTER_SERVER_ENDPOINT_COUNT (0)
@@ -281,6 +282,7 @@
#define MATTER_DM_ECOSYSTEM_INFORMATION_CLUSTER_CLIENT_ENDPOINT_COUNT (0)
#define MATTER_DM_COMMISSIONER_CONTROL_CLUSTER_CLIENT_ENDPOINT_COUNT (0)
#define MATTER_DM_TLS_CERTIFICATE_MANAGEMENT_CLUSTER_CLIENT_ENDPOINT_COUNT (0)
+#define MATTER_DM_METER_IDENTIFICATION_CLUSTER_CLIENT_ENDPOINT_COUNT (0)
#define MATTER_DM_UNIT_TESTING_CLUSTER_CLIENT_ENDPOINT_COUNT (0)
#define MATTER_DM_FAULT_INJECTION_CLUSTER_CLIENT_ENDPOINT_COUNT (0)
#define MATTER_DM_SAMPLE_MEI_CLUSTER_CLIENT_ENDPOINT_COUNT (0)
diff --git a/scripts/tools/zap/tests/outputs/lighting-app/app-templates/gen_config.h b/scripts/tools/zap/tests/outputs/lighting-app/app-templates/gen_config.h
index c82d5985948518..571f1cf1e63a95 100644
--- a/scripts/tools/zap/tests/outputs/lighting-app/app-templates/gen_config.h
+++ b/scripts/tools/zap/tests/outputs/lighting-app/app-templates/gen_config.h
@@ -149,6 +149,7 @@
#define MATTER_DM_ECOSYSTEM_INFORMATION_CLUSTER_SERVER_ENDPOINT_COUNT (0)
#define MATTER_DM_COMMISSIONER_CONTROL_CLUSTER_SERVER_ENDPOINT_COUNT (0)
#define MATTER_DM_TLS_CERTIFICATE_MANAGEMENT_CLUSTER_SERVER_ENDPOINT_COUNT (0)
+#define MATTER_DM_METER_IDENTIFICATION_CLUSTER_SERVER_ENDPOINT_COUNT (0)
#define MATTER_DM_UNIT_TESTING_CLUSTER_SERVER_ENDPOINT_COUNT (0)
#define MATTER_DM_FAULT_INJECTION_CLUSTER_SERVER_ENDPOINT_COUNT (0)
#define MATTER_DM_SAMPLE_MEI_CLUSTER_SERVER_ENDPOINT_COUNT (0)
@@ -281,6 +282,7 @@
#define MATTER_DM_ECOSYSTEM_INFORMATION_CLUSTER_CLIENT_ENDPOINT_COUNT (0)
#define MATTER_DM_COMMISSIONER_CONTROL_CLUSTER_CLIENT_ENDPOINT_COUNT (0)
#define MATTER_DM_TLS_CERTIFICATE_MANAGEMENT_CLUSTER_CLIENT_ENDPOINT_COUNT (0)
+#define MATTER_DM_METER_IDENTIFICATION_CLUSTER_CLIENT_ENDPOINT_COUNT (0)
#define MATTER_DM_UNIT_TESTING_CLUSTER_CLIENT_ENDPOINT_COUNT (0)
#define MATTER_DM_FAULT_INJECTION_CLUSTER_CLIENT_ENDPOINT_COUNT (0)
#define MATTER_DM_SAMPLE_MEI_CLUSTER_CLIENT_ENDPOINT_COUNT (0)
diff --git a/src/app/clusters/meter-identification-server/MeterIdentificationTestEventTriggerHandler.h b/src/app/clusters/meter-identification-server/MeterIdentificationTestEventTriggerHandler.h
new file mode 100644
index 00000000000000..ceac4547a1c430
--- /dev/null
+++ b/src/app/clusters/meter-identification-server/MeterIdentificationTestEventTriggerHandler.h
@@ -0,0 +1,73 @@
+/*
+ *
+ * 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
+
+ /**
+ * @brief User handler for handling the test event trigger
+ *
+ * @note If TestEventTrigger is enabled, it needs to be implemented in the app
+ *
+ * @param eventTrigger Event trigger to handle
+ *
+ * @retval true on success
+ * @retval false if error happened
+ */
+ bool HandleMeterIdentificationTestEventTrigger(uint64_t eventTrigger);
+
+ namespace chip {
+
+ /*
+ * These Test EventTrigger values are specified in the TC_MTRID test plan
+ * and are defined conditions used in test events.
+ *
+ * They are sent along with the enableKey (manufacturer defined secret)
+ * in the General Diagnostic cluster TestEventTrigger command
+ */
+ enum class MeterIdentificationTrigger : uint64_t
+ {
+ // Scenarios
+ // Attributes Value Update Test Event | Increment attribute values
+ kAttributesValueUpdate = 0x0b06000000000000,
+ // Attributes Value Test Event Clear | Return the device to pre-test status
+ kAttributesValueUpdateClear = 0x0b06000000000001,
+ };
+
+ class MeterIdentificationTestEventTriggerHandler : public TestEventTriggerHandler
+ {
+ public:
+ explicit MeterIdentificationTestEventTriggerHandler() {}
+
+ /** This function must return True if the eventTrigger is recognised and handled
+ * It must return False to allow a higher level TestEvent handler to check other
+ * clusters that may handle it.
+ */
+ CHIP_ERROR HandleEventTrigger(uint64_t eventTrigger) override
+ {
+ if (HandleMeterIdentificationTestEventTrigger(eventTrigger))
+ {
+ return CHIP_NO_ERROR;
+ }
+ return CHIP_ERROR_INVALID_ARGUMENT;
+ }
+ };
+
+ } // namespace chip
+
\ No newline at end of file
diff --git a/src/app/clusters/meter-identification-server/meter-identification-server.cpp b/src/app/clusters/meter-identification-server/meter-identification-server.cpp
new file mode 100644
index 00000000000000..84ca9889f77552
--- /dev/null
+++ b/src/app/clusters/meter-identification-server/meter-identification-server.cpp
@@ -0,0 +1,123 @@
+/*
+ *
+ * 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.
+ */
+#include "meter-identification-server.h"
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace chip;
+using namespace chip::app;
+using namespace chip::app::DataModel;
+using namespace chip::app::Clusters;
+using namespace chip::app::Clusters::MeterIdentification;
+using namespace chip::app::Clusters::MeterIdentification::Attributes;
+
+using chip::Protocols::InteractionModel::Status;
+
+namespace chip {
+namespace app {
+namespace Clusters {
+namespace MeterIdentification {
+
+CHIP_ERROR Instance::Init()
+{
+ VerifyOrReturnError(AttributeAccessInterfaceRegistry::Instance().Register(this), CHIP_ERROR_INCORRECT_STATE);
+ return CHIP_NO_ERROR;
+}
+
+void Instance::Shutdown()
+{
+ AttributeAccessInterfaceRegistry::Instance().Unregister(this);
+}
+
+bool Instance::HasFeature(const Feature & aFeature) const
+{
+ return mFeature.Has(aFeature);
+}
+
+// static CHIP_ERROR EncodeStringOnSuccess(CHIP_ERROR status, AttributeValueEncoder & encoder, const char * buf, size_t maxBufSize)
+//{
+// ReturnErrorOnFailure(status);
+// return encoder.Encode(chip::CharSpan(buf, strnlen(buf, maxBufSize)));
+// }
+
+// AttributeAccessInterface
+CHIP_ERROR Instance::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder)
+{
+ ChipLogProgress(Zcl, "Meter Indication read attr %d", aPath.mAttributeId);
+
+ switch (aPath.mAttributeId)
+ {
+ case FeatureMap::Id:
+ ReturnErrorOnFailure(aEncoder.Encode(mFeature));
+ break;
+ case MeterType::Id:
+ ReturnErrorOnFailure(aEncoder.Encode(mDelegate.GetMeterType()));
+ break;
+
+ case PointOfDelivery::Id:
+ ReturnErrorOnFailure(aEncoder.Encode(mDelegate.GetPointOfDelivery()));
+ break;
+
+ case MeterSerialNumber::Id:
+ ReturnErrorOnFailure(aEncoder.Encode(mDelegate.GetMeterSerialNumber()));
+ break;
+
+ case ProtocolVersion::Id:
+ ReturnErrorOnFailure(aEncoder.Encode(mDelegate.GetProtocolVersion()));
+ break;
+
+ case PowerThreshold::Id:
+ if (HasFeature(Feature::kPowerThreshold))
+ ReturnErrorOnFailure(aEncoder.Encode(mDelegate.GetPowerThreshold()));
+ else
+ return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE;
+ break;
+ }
+ return CHIP_NO_ERROR;
+}
+
+CHIP_ERROR Instance::Write(const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder)
+{
+ /*
+
+ switch (aPath.mAttributeId)
+ {
+
+ default:
+ break;
+ }
+ */
+ return CHIP_NO_ERROR;
+}
+
+} // namespace MeterIdentification
+} // namespace Clusters
+} // namespace app
+} // namespace chip
+
+// -----------------------------------------------------------------------------
+// Plugin initialization
+
+void MatterMeterIdentificationPluginServerInitCallback() {}
diff --git a/src/app/clusters/meter-identification-server/meter-identification-server.h b/src/app/clusters/meter-identification-server/meter-identification-server.h
new file mode 100644
index 00000000000000..279dde4f5d264d
--- /dev/null
+++ b/src/app/clusters/meter-identification-server/meter-identification-server.h
@@ -0,0 +1,83 @@
+/**
+ *
+ * Copyright (c) 2024 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
+
+using chip::app::Clusters::MeterIdentification::MeterTypeEnum;
+using Feature = chip::app::Clusters::MeterIdentification::Feature;
+
+namespace chip {
+namespace app {
+namespace Clusters {
+namespace MeterIdentification {
+
+struct Delegate
+{
+public:
+ virtual ~Delegate() = default;
+
+ void SetEndpointId(const EndpointId & aEndpoint) { mEndpointId = aEndpoint; }
+
+ virtual DataModel::Nullable GetMeterType() = 0;
+ virtual DataModel::Nullable GetPointOfDelivery() = 0;
+ virtual DataModel::Nullable GetMeterSerialNumber() = 0;
+ virtual DataModel::Nullable GetProtocolVersion() = 0;
+ virtual DataModel::Nullable GetPowerThreshold() = 0;
+
+protected:
+ EndpointId mEndpointId = 0;
+};
+
+class Instance : public AttributeAccessInterface
+{
+public:
+ Instance(const EndpointId & aEndpointId, Delegate & aDelegate, const BitMask & aFeature) :
+ AttributeAccessInterface(MakeOptional(aEndpointId), Id), mDelegate(aDelegate), mFeature(aFeature)
+ {
+ /* set the base class delegates endpointId */
+ mDelegate.SetEndpointId(aEndpointId);
+ }
+ ~Instance() { Shutdown(); }
+
+ CHIP_ERROR Init();
+ void Shutdown();
+
+ bool HasFeature(const Feature & aFeature) const;
+
+private:
+ Delegate & mDelegate;
+ BitMask mFeature;
+
+ // AttributeAccessInterface
+ CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override;
+ CHIP_ERROR Write(const ConcreteDataAttributePath & aPath, AttributeValueDecoder & aDecoder) override;
+};
+
+} // namespace MeterIdentification
+} // namespace Clusters
+} // namespace app
+} // namespace chip
\ No newline at end of file
diff --git a/src/app/zap-templates/zcl/data-model/chip/meter-identification-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/meter-identification-cluster.xml
new file mode 100644
index 00000000000000..0d98cfe540337b
--- /dev/null
+++ b/src/app/zap-templates/zcl/data-model/chip/meter-identification-cluster.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ General
+ Meter Identification
+ 0x0B06
+ METER_IDENTIFICATION_CLUSTER
+
+ true
+
+
+
+
+
+ true
+
+ MeterType
+ PointOfDelivery
+ MeterSerialNumber
+ ProtocolVersion
+ PowerThreshold
+
+
diff --git a/src/app/zap-templates/zcl/zcl-with-test-extensions.json b/src/app/zap-templates/zcl/zcl-with-test-extensions.json
index b5addfc2fcb105..912db9b2d5b002 100644
--- a/src/app/zap-templates/zcl/zcl-with-test-extensions.json
+++ b/src/app/zap-templates/zcl/zcl-with-test-extensions.json
@@ -47,6 +47,7 @@
"dishwasher-alarm-cluster.xml",
"dishwasher-mode-cluster.xml",
"drlc-cluster.xml",
+ "meter-identification-cluster.xml",
"microwave-oven-mode-cluster.xml",
"microwave-oven-control-cluster.xml",
"door-lock-cluster.xml",
@@ -678,6 +679,7 @@
"FeatureMap",
"ClusterRevision"
],
+ "Meter Identification": ["PowerThreshold"],
"Water Heater Mode": ["SupportedModes", "CurrentMode", "FeatureMap"],
"Wi-Fi Network Management": ["SSID", "PassphraseSurrogate"],
"Thread Network Directory": [
diff --git a/src/app/zap-templates/zcl/zcl.json b/src/app/zap-templates/zcl/zcl.json
index 49b9fd84f5a483..8f4127d19200f7 100644
--- a/src/app/zap-templates/zcl/zcl.json
+++ b/src/app/zap-templates/zcl/zcl.json
@@ -41,6 +41,7 @@
"diagnostic-logs-cluster.xml",
"dishwasher-alarm-cluster.xml",
"dishwasher-mode-cluster.xml",
+ "meter-identification-cluster.xml",
"microwave-oven-mode-cluster.xml",
"door-lock-cluster.xml",
"drlc-cluster.xml",
@@ -672,6 +673,7 @@
"FeatureMap",
"ClusterRevision"
],
+ "Meter Identification": ["PowerThreshold"],
"Water Heater Mode": ["SupportedModes", "CurrentMode", "FeatureMap"],
"Wi-Fi Network Management": ["SSID", "PassphraseSurrogate"],
"Thread Network Directory": [
diff --git a/src/app/zap_cluster_list.json b/src/app/zap_cluster_list.json
index 2eb53f9c9758cc..eb98b4d9ede0e4 100644
--- a/src/app/zap_cluster_list.json
+++ b/src/app/zap_cluster_list.json
@@ -37,6 +37,7 @@
"DIAGNOSTIC_LOGS_CLUSTER": [],
"DISHWASHER_ALARM_CLUSTER": [],
"DISHWASHER_MODE_CLUSTER": [],
+ "METER_IDENTIFICATION_CLUSTER": [],
"MICROWAVE_OVEN_MODE_CLUSTER": [],
"DOOR_LOCK_CLUSTER": [],
"ECOSYSTEM_INFORMATION_CLUSTER": [],
@@ -191,6 +192,7 @@
"DIAGNOSTIC_LOGS_CLUSTER": ["diagnostic-logs-server"],
"DISHWASHER_ALARM_CLUSTER": ["dishwasher-alarm-server"],
"DISHWASHER_MODE_CLUSTER": ["mode-base-server"],
+ "METER_IDENTIFICATION_CLUSTER": ["meter-identification-server"],
"MICROWAVE_OVEN_MODE_CLUSTER": ["mode-base-server"],
"DOOR_LOCK_CLUSTER": ["door-lock-server"],
"ECOSYSTEM_INFORMATION_CLUSTER": ["ecosystem-information-server"],
diff --git a/src/controller/data_model/controller-clusters.matter b/src/controller/data_model/controller-clusters.matter
index d2ef221de95353..75043ab8752e7d 100644
--- a/src/controller/data_model/controller-clusters.matter
+++ b/src/controller/data_model/controller-clusters.matter
@@ -10696,6 +10696,45 @@ provisional cluster TlsCertificateManagement = 2049 {
command access(invoke: administer) RemoveClientCertificate(RemoveClientCertificateRequest): DefaultSuccess = 15;
}
+/** */
+cluster MeterIdentification = 2822 {
+ revision 1;
+
+ enum MeterTypeEnum : enum8 {
+ kUtility = 0;
+ kPrivate = 1;
+ kGeneric = 2;
+ }
+
+ enum PowerThresholdSourceEnum : enum8 {
+ kContract = 0;
+ kRegulator = 1;
+ kEquipment = 2;
+ }
+
+ bitmap Feature : bitmap32 {
+ kPowerThreshold = 0x1;
+ }
+
+ struct PowerThresholdStruct {
+ optional int64s powerThreshold = 0;
+ optional int64s apparentPowerThreshold = 1;
+ nullable PowerThresholdSourceEnum powerThresholdSource = 2;
+ }
+
+ readonly attribute nullable MeterTypeEnum meterType = 0;
+ readonly attribute nullable char_string<64> pointOfDelivery = 1;
+ readonly attribute nullable char_string<64> meterSerialNumber = 2;
+ readonly attribute optional nullable char_string<64> protocolVersion = 3;
+ readonly attribute optional nullable PowerThresholdStruct powerThreshold = 4;
+ readonly attribute command_id generatedCommandList[] = 65528;
+ readonly attribute command_id acceptedCommandList[] = 65529;
+ readonly attribute event_id eventList[] = 65530;
+ readonly attribute attrib_id attributeList[] = 65531;
+ readonly attribute bitmap32 featureMap = 65532;
+ readonly attribute int16u clusterRevision = 65533;
+}
+
/** The Test Cluster is meant to validate the generated code */
internal cluster UnitTesting = 4294048773 {
revision 1; // NOTE: Default/not specifically set
diff --git a/src/controller/data_model/controller-clusters.zap b/src/controller/data_model/controller-clusters.zap
index 87ef155a4bc0a2..24b0c5732058a6 100644
--- a/src/controller/data_model/controller-clusters.zap
+++ b/src/controller/data_model/controller-clusters.zap
@@ -2976,6 +2976,144 @@
}
]
},
+ {
+ "name": "Meter Identification",
+ "code": 2822,
+ "mfgCode": null,
+ "define": "METER_IDENTIFICATION_CLUSTER",
+ "side": "server",
+ "enabled": 1,
+ "attributes": [
+ {
+ "name": "MeterType",
+ "code": 0,
+ "mfgCode": null,
+ "side": "server",
+ "type": "MeterTypeEnum",
+ "included": 1,
+ "storageOption": "RAM",
+ "singleton": 0,
+ "bounded": 0,
+ "defaultValue": "",
+ "reportable": 1,
+ "minInterval": 1,
+ "maxInterval": 65534,
+ "reportableChange": 0
+ },
+ {
+ "name": "PointOfDelivery",
+ "code": 1,
+ "mfgCode": null,
+ "side": "server",
+ "type": "char_string",
+ "included": 1,
+ "storageOption": "RAM",
+ "singleton": 0,
+ "bounded": 0,
+ "defaultValue": "",
+ "reportable": 1,
+ "minInterval": 1,
+ "maxInterval": 65534,
+ "reportableChange": 0
+ },
+ {
+ "name": "MeterSerialNumber",
+ "code": 2,
+ "mfgCode": null,
+ "side": "server",
+ "type": "char_string",
+ "included": 1,
+ "storageOption": "RAM",
+ "singleton": 0,
+ "bounded": 0,
+ "defaultValue": "",
+ "reportable": 1,
+ "minInterval": 1,
+ "maxInterval": 65534,
+ "reportableChange": 0
+ },
+ {
+ "name": "GeneratedCommandList",
+ "code": 65528,
+ "mfgCode": null,
+ "side": "server",
+ "type": "array",
+ "included": 1,
+ "storageOption": "External",
+ "singleton": 0,
+ "bounded": 0,
+ "defaultValue": "",
+ "reportable": 1,
+ "minInterval": 1,
+ "maxInterval": 65534,
+ "reportableChange": 0
+ },
+ {
+ "name": "AcceptedCommandList",
+ "code": 65529,
+ "mfgCode": null,
+ "side": "server",
+ "type": "array",
+ "included": 1,
+ "storageOption": "External",
+ "singleton": 0,
+ "bounded": 0,
+ "defaultValue": "",
+ "reportable": 1,
+ "minInterval": 1,
+ "maxInterval": 65534,
+ "reportableChange": 0
+ },
+ {
+ "name": "AttributeList",
+ "code": 65531,
+ "mfgCode": null,
+ "side": "server",
+ "type": "array",
+ "included": 1,
+ "storageOption": "External",
+ "singleton": 0,
+ "bounded": 0,
+ "defaultValue": "",
+ "reportable": 1,
+ "minInterval": 1,
+ "maxInterval": 65534,
+ "reportableChange": 0
+ },
+ {
+ "name": "FeatureMap",
+ "code": 65532,
+ "mfgCode": null,
+ "side": "server",
+ "type": "bitmap32",
+ "included": 1,
+ "storageOption": "RAM",
+ "singleton": 0,
+ "bounded": 0,
+ "defaultValue": "0",
+ "reportable": 1,
+ "minInterval": 1,
+ "maxInterval": 65534,
+ "reportableChange": 0
+ },
+ {
+ "name": "ClusterRevision",
+ "code": 65533,
+ "mfgCode": null,
+ "side": "server",
+ "type": "int16u",
+ "included": 1,
+ "storageOption": "RAM",
+ "singleton": 0,
+ "bounded": 0,
+ "defaultValue": "1",
+ "reportable": 1,
+ "minInterval": 1,
+ "maxInterval": 65534,
+ "reportableChange": 0
+ }
+ ]
+ },
{
"name": "Unit Testing",
"code": 4294048773,
diff --git a/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java b/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java
index 5b7dceafbab202..c867e1f5b25a62 100644
--- a/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java
+++ b/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java
@@ -65645,6 +65645,354 @@ public void onSuccess(byte[] tlv) {
}
}
+ public static class MeterIdentificationCluster extends BaseChipCluster {
+ public static final long CLUSTER_ID = 2822L;
+
+ private static final long METER_TYPE_ATTRIBUTE_ID = 0L;
+ private static final long POINT_OF_DELIVERY_ATTRIBUTE_ID = 1L;
+ private static final long METER_SERIAL_NUMBER_ATTRIBUTE_ID = 2L;
+ private static final long PROTOCOL_VERSION_ATTRIBUTE_ID = 3L;
+ private static final long POWER_THRESHOLD_ATTRIBUTE_ID = 4L;
+ private static final long GENERATED_COMMAND_LIST_ATTRIBUTE_ID = 65528L;
+ private static final long ACCEPTED_COMMAND_LIST_ATTRIBUTE_ID = 65529L;
+ private static final long EVENT_LIST_ATTRIBUTE_ID = 65530L;
+ private static final long ATTRIBUTE_LIST_ATTRIBUTE_ID = 65531L;
+ private static final long FEATURE_MAP_ATTRIBUTE_ID = 65532L;
+ private static final long CLUSTER_REVISION_ATTRIBUTE_ID = 65533L;
+
+ public MeterIdentificationCluster(long devicePtr, int endpointId) {
+ super(devicePtr, endpointId, CLUSTER_ID);
+ }
+
+ @Override
+ @Deprecated
+ public long initWithDevice(long devicePtr, int endpointId) {
+ return 0L;
+ }
+
+ public interface MeterTypeAttributeCallback extends BaseAttributeCallback {
+ void onSuccess(@Nullable Integer value);
+ }
+
+ public interface PointOfDeliveryAttributeCallback extends BaseAttributeCallback {
+ void onSuccess(@Nullable String value);
+ }
+
+ public interface MeterSerialNumberAttributeCallback extends BaseAttributeCallback {
+ void onSuccess(@Nullable String value);
+ }
+
+ public interface ProtocolVersionAttributeCallback extends BaseAttributeCallback {
+ void onSuccess(@Nullable String value);
+ }
+
+ public interface PowerThresholdAttributeCallback extends BaseAttributeCallback {
+ void onSuccess(@Nullable ChipStructs.MeterIdentificationClusterPowerThresholdStruct value);
+ }
+
+ public interface GeneratedCommandListAttributeCallback extends BaseAttributeCallback {
+ void onSuccess(List value);
+ }
+
+ public interface AcceptedCommandListAttributeCallback extends BaseAttributeCallback {
+ void onSuccess(List value);
+ }
+
+ public interface EventListAttributeCallback extends BaseAttributeCallback {
+ void onSuccess(List value);
+ }
+
+ public interface AttributeListAttributeCallback extends BaseAttributeCallback {
+ void onSuccess(List value);
+ }
+
+ public void readMeterTypeAttribute(
+ MeterTypeAttributeCallback callback) {
+ ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, METER_TYPE_ATTRIBUTE_ID);
+
+ readAttribute(new ReportCallbackImpl(callback, path) {
+ @Override
+ public void onSuccess(byte[] tlv) {
+ @Nullable Integer value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
+ callback.onSuccess(value);
+ }
+ }, METER_TYPE_ATTRIBUTE_ID, true);
+ }
+
+ public void subscribeMeterTypeAttribute(
+ MeterTypeAttributeCallback callback, int minInterval, int maxInterval) {
+ ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, METER_TYPE_ATTRIBUTE_ID);
+
+ subscribeAttribute(new ReportCallbackImpl(callback, path) {
+ @Override
+ public void onSuccess(byte[] tlv) {
+ @Nullable Integer value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
+ callback.onSuccess(value);
+ }
+ }, METER_TYPE_ATTRIBUTE_ID, minInterval, maxInterval);
+ }
+
+ public void readPointOfDeliveryAttribute(
+ PointOfDeliveryAttributeCallback callback) {
+ ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, POINT_OF_DELIVERY_ATTRIBUTE_ID);
+
+ readAttribute(new ReportCallbackImpl(callback, path) {
+ @Override
+ public void onSuccess(byte[] tlv) {
+ @Nullable String value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
+ callback.onSuccess(value);
+ }
+ }, POINT_OF_DELIVERY_ATTRIBUTE_ID, true);
+ }
+
+ public void subscribePointOfDeliveryAttribute(
+ PointOfDeliveryAttributeCallback callback, int minInterval, int maxInterval) {
+ ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, POINT_OF_DELIVERY_ATTRIBUTE_ID);
+
+ subscribeAttribute(new ReportCallbackImpl(callback, path) {
+ @Override
+ public void onSuccess(byte[] tlv) {
+ @Nullable String value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
+ callback.onSuccess(value);
+ }
+ }, POINT_OF_DELIVERY_ATTRIBUTE_ID, minInterval, maxInterval);
+ }
+
+ public void readMeterSerialNumberAttribute(
+ MeterSerialNumberAttributeCallback callback) {
+ ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, METER_SERIAL_NUMBER_ATTRIBUTE_ID);
+
+ readAttribute(new ReportCallbackImpl(callback, path) {
+ @Override
+ public void onSuccess(byte[] tlv) {
+ @Nullable String value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
+ callback.onSuccess(value);
+ }
+ }, METER_SERIAL_NUMBER_ATTRIBUTE_ID, true);
+ }
+
+ public void subscribeMeterSerialNumberAttribute(
+ MeterSerialNumberAttributeCallback callback, int minInterval, int maxInterval) {
+ ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, METER_SERIAL_NUMBER_ATTRIBUTE_ID);
+
+ subscribeAttribute(new ReportCallbackImpl(callback, path) {
+ @Override
+ public void onSuccess(byte[] tlv) {
+ @Nullable String value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
+ callback.onSuccess(value);
+ }
+ }, METER_SERIAL_NUMBER_ATTRIBUTE_ID, minInterval, maxInterval);
+ }
+
+ public void readProtocolVersionAttribute(
+ ProtocolVersionAttributeCallback callback) {
+ ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, PROTOCOL_VERSION_ATTRIBUTE_ID);
+
+ readAttribute(new ReportCallbackImpl(callback, path) {
+ @Override
+ public void onSuccess(byte[] tlv) {
+ @Nullable String value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
+ callback.onSuccess(value);
+ }
+ }, PROTOCOL_VERSION_ATTRIBUTE_ID, true);
+ }
+
+ public void subscribeProtocolVersionAttribute(
+ ProtocolVersionAttributeCallback callback, int minInterval, int maxInterval) {
+ ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, PROTOCOL_VERSION_ATTRIBUTE_ID);
+
+ subscribeAttribute(new ReportCallbackImpl(callback, path) {
+ @Override
+ public void onSuccess(byte[] tlv) {
+ @Nullable String value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
+ callback.onSuccess(value);
+ }
+ }, PROTOCOL_VERSION_ATTRIBUTE_ID, minInterval, maxInterval);
+ }
+
+ public void readPowerThresholdAttribute(
+ PowerThresholdAttributeCallback callback) {
+ ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, POWER_THRESHOLD_ATTRIBUTE_ID);
+
+ readAttribute(new ReportCallbackImpl(callback, path) {
+ @Override
+ public void onSuccess(byte[] tlv) {
+ @Nullable ChipStructs.MeterIdentificationClusterPowerThresholdStruct value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
+ callback.onSuccess(value);
+ }
+ }, POWER_THRESHOLD_ATTRIBUTE_ID, true);
+ }
+
+ public void subscribePowerThresholdAttribute(
+ PowerThresholdAttributeCallback callback, int minInterval, int maxInterval) {
+ ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, POWER_THRESHOLD_ATTRIBUTE_ID);
+
+ subscribeAttribute(new ReportCallbackImpl(callback, path) {
+ @Override
+ public void onSuccess(byte[] tlv) {
+ @Nullable ChipStructs.MeterIdentificationClusterPowerThresholdStruct value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
+ callback.onSuccess(value);
+ }
+ }, POWER_THRESHOLD_ATTRIBUTE_ID, minInterval, maxInterval);
+ }
+
+ public void readGeneratedCommandListAttribute(
+ GeneratedCommandListAttributeCallback callback) {
+ ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, GENERATED_COMMAND_LIST_ATTRIBUTE_ID);
+
+ readAttribute(new ReportCallbackImpl(callback, path) {
+ @Override
+ public void onSuccess(byte[] tlv) {
+ List value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
+ callback.onSuccess(value);
+ }
+ }, GENERATED_COMMAND_LIST_ATTRIBUTE_ID, true);
+ }
+
+ public void subscribeGeneratedCommandListAttribute(
+ GeneratedCommandListAttributeCallback callback, int minInterval, int maxInterval) {
+ ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, GENERATED_COMMAND_LIST_ATTRIBUTE_ID);
+
+ subscribeAttribute(new ReportCallbackImpl(callback, path) {
+ @Override
+ public void onSuccess(byte[] tlv) {
+ List value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
+ callback.onSuccess(value);
+ }
+ }, GENERATED_COMMAND_LIST_ATTRIBUTE_ID, minInterval, maxInterval);
+ }
+
+ public void readAcceptedCommandListAttribute(
+ AcceptedCommandListAttributeCallback callback) {
+ ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, ACCEPTED_COMMAND_LIST_ATTRIBUTE_ID);
+
+ readAttribute(new ReportCallbackImpl(callback, path) {
+ @Override
+ public void onSuccess(byte[] tlv) {
+ List value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
+ callback.onSuccess(value);
+ }
+ }, ACCEPTED_COMMAND_LIST_ATTRIBUTE_ID, true);
+ }
+
+ public void subscribeAcceptedCommandListAttribute(
+ AcceptedCommandListAttributeCallback callback, int minInterval, int maxInterval) {
+ ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, ACCEPTED_COMMAND_LIST_ATTRIBUTE_ID);
+
+ subscribeAttribute(new ReportCallbackImpl(callback, path) {
+ @Override
+ public void onSuccess(byte[] tlv) {
+ List value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
+ callback.onSuccess(value);
+ }
+ }, ACCEPTED_COMMAND_LIST_ATTRIBUTE_ID, minInterval, maxInterval);
+ }
+
+ public void readEventListAttribute(
+ EventListAttributeCallback callback) {
+ ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, EVENT_LIST_ATTRIBUTE_ID);
+
+ readAttribute(new ReportCallbackImpl(callback, path) {
+ @Override
+ public void onSuccess(byte[] tlv) {
+ List value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
+ callback.onSuccess(value);
+ }
+ }, EVENT_LIST_ATTRIBUTE_ID, true);
+ }
+
+ public void subscribeEventListAttribute(
+ EventListAttributeCallback callback, int minInterval, int maxInterval) {
+ ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, EVENT_LIST_ATTRIBUTE_ID);
+
+ subscribeAttribute(new ReportCallbackImpl(callback, path) {
+ @Override
+ public void onSuccess(byte[] tlv) {
+ List value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
+ callback.onSuccess(value);
+ }
+ }, EVENT_LIST_ATTRIBUTE_ID, minInterval, maxInterval);
+ }
+
+ public void readAttributeListAttribute(
+ AttributeListAttributeCallback callback) {
+ ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, ATTRIBUTE_LIST_ATTRIBUTE_ID);
+
+ readAttribute(new ReportCallbackImpl(callback, path) {
+ @Override
+ public void onSuccess(byte[] tlv) {
+ List value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
+ callback.onSuccess(value);
+ }
+ }, ATTRIBUTE_LIST_ATTRIBUTE_ID, true);
+ }
+
+ public void subscribeAttributeListAttribute(
+ AttributeListAttributeCallback callback, int minInterval, int maxInterval) {
+ ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, ATTRIBUTE_LIST_ATTRIBUTE_ID);
+
+ subscribeAttribute(new ReportCallbackImpl(callback, path) {
+ @Override
+ public void onSuccess(byte[] tlv) {
+ List value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
+ callback.onSuccess(value);
+ }
+ }, ATTRIBUTE_LIST_ATTRIBUTE_ID, minInterval, maxInterval);
+ }
+
+ public void readFeatureMapAttribute(
+ LongAttributeCallback callback) {
+ ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, FEATURE_MAP_ATTRIBUTE_ID);
+
+ readAttribute(new ReportCallbackImpl(callback, path) {
+ @Override
+ public void onSuccess(byte[] tlv) {
+ Long value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
+ callback.onSuccess(value);
+ }
+ }, FEATURE_MAP_ATTRIBUTE_ID, true);
+ }
+
+ public void subscribeFeatureMapAttribute(
+ LongAttributeCallback callback, int minInterval, int maxInterval) {
+ ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, FEATURE_MAP_ATTRIBUTE_ID);
+
+ subscribeAttribute(new ReportCallbackImpl(callback, path) {
+ @Override
+ public void onSuccess(byte[] tlv) {
+ Long value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
+ callback.onSuccess(value);
+ }
+ }, FEATURE_MAP_ATTRIBUTE_ID, minInterval, maxInterval);
+ }
+
+ public void readClusterRevisionAttribute(
+ IntegerAttributeCallback callback) {
+ ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, CLUSTER_REVISION_ATTRIBUTE_ID);
+
+ readAttribute(new ReportCallbackImpl(callback, path) {
+ @Override
+ public void onSuccess(byte[] tlv) {
+ Integer value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
+ callback.onSuccess(value);
+ }
+ }, CLUSTER_REVISION_ATTRIBUTE_ID, true);
+ }
+
+ public void subscribeClusterRevisionAttribute(
+ IntegerAttributeCallback callback, int minInterval, int maxInterval) {
+ ChipAttributePath path = ChipAttributePath.newInstance(endpointId, clusterId, CLUSTER_REVISION_ATTRIBUTE_ID);
+
+ subscribeAttribute(new ReportCallbackImpl(callback, path) {
+ @Override
+ public void onSuccess(byte[] tlv) {
+ Integer value = ChipTLVValueDecoder.decodeAttributeValue(path, tlv);
+ callback.onSuccess(value);
+ }
+ }, CLUSTER_REVISION_ATTRIBUTE_ID, minInterval, maxInterval);
+ }
+ }
+
public static class UnitTestingCluster extends BaseChipCluster {
public static final long CLUSTER_ID = 4294048773L;
diff --git a/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java b/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java
index c8b32f35f2ba4c..4cfb79b512897d 100644
--- a/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java
+++ b/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java
@@ -16286,6 +16286,82 @@ public String toString() {
return output.toString();
}
}
+public static class MeterIdentificationClusterPowerThresholdStruct {
+ public Optional powerThreshold;
+ public Optional apparentPowerThreshold;
+ public @Nullable Integer powerThresholdSource;
+ private static final long POWER_THRESHOLD_ID = 0L;
+ private static final long APPARENT_POWER_THRESHOLD_ID = 1L;
+ private static final long POWER_THRESHOLD_SOURCE_ID = 2L;
+
+ public MeterIdentificationClusterPowerThresholdStruct(
+ Optional powerThreshold,
+ Optional apparentPowerThreshold,
+ @Nullable Integer powerThresholdSource
+ ) {
+ this.powerThreshold = powerThreshold;
+ this.apparentPowerThreshold = apparentPowerThreshold;
+ this.powerThresholdSource = powerThresholdSource;
+ }
+
+ public StructType encodeTlv() {
+ ArrayList values = new ArrayList<>();
+ values.add(new StructElement(POWER_THRESHOLD_ID, powerThreshold.map((nonOptionalpowerThreshold) -> new IntType(nonOptionalpowerThreshold)).orElse(new EmptyType())));
+ values.add(new StructElement(APPARENT_POWER_THRESHOLD_ID, apparentPowerThreshold.map((nonOptionalapparentPowerThreshold) -> new IntType(nonOptionalapparentPowerThreshold)).orElse(new EmptyType())));
+ values.add(new StructElement(POWER_THRESHOLD_SOURCE_ID, powerThresholdSource != null ? new UIntType(powerThresholdSource) : new NullType()));
+
+ return new StructType(values);
+ }
+
+ public static MeterIdentificationClusterPowerThresholdStruct decodeTlv(BaseTLVType tlvValue) {
+ if (tlvValue == null || tlvValue.type() != TLVType.Struct) {
+ return null;
+ }
+ Optional powerThreshold = Optional.empty();
+ Optional apparentPowerThreshold = Optional.empty();
+ @Nullable Integer powerThresholdSource = null;
+ for (StructElement element: ((StructType)tlvValue).value()) {
+ if (element.contextTagNum() == POWER_THRESHOLD_ID) {
+ if (element.value(BaseTLVType.class).type() == TLVType.Int) {
+ IntType castingValue = element.value(IntType.class);
+ powerThreshold = Optional.of(castingValue.value(Long.class));
+ }
+ } else if (element.contextTagNum() == APPARENT_POWER_THRESHOLD_ID) {
+ if (element.value(BaseTLVType.class).type() == TLVType.Int) {
+ IntType castingValue = element.value(IntType.class);
+ apparentPowerThreshold = Optional.of(castingValue.value(Long.class));
+ }
+ } else if (element.contextTagNum() == POWER_THRESHOLD_SOURCE_ID) {
+ if (element.value(BaseTLVType.class).type() == TLVType.UInt) {
+ UIntType castingValue = element.value(UIntType.class);
+ powerThresholdSource = castingValue.value(Integer.class);
+ }
+ }
+ }
+ return new MeterIdentificationClusterPowerThresholdStruct(
+ powerThreshold,
+ apparentPowerThreshold,
+ powerThresholdSource
+ );
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder output = new StringBuilder();
+ output.append("MeterIdentificationClusterPowerThresholdStruct {\n");
+ output.append("\tpowerThreshold: ");
+ output.append(powerThreshold);
+ output.append("\n");
+ output.append("\tapparentPowerThreshold: ");
+ output.append(apparentPowerThreshold);
+ output.append("\n");
+ output.append("\tpowerThresholdSource: ");
+ output.append(powerThresholdSource);
+ output.append("\n");
+ output.append("}\n");
+ return output.toString();
+ }
+}
public static class UnitTestingClusterSimpleStruct {
public Integer a;
public Boolean b;
diff --git a/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java
index 64242e9ab049a7..326660a0bb2905 100644
--- a/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java
+++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java
@@ -412,6 +412,9 @@ public static BaseCluster getCluster(long clusterId) {
if (clusterId == TlsCertificateManagement.ID) {
return new TlsCertificateManagement();
}
+ if (clusterId == MeterIdentification.ID) {
+ return new MeterIdentification();
+ }
if (clusterId == UnitTesting.ID) {
return new UnitTesting();
}
@@ -19006,6 +19009,111 @@ public long getCommandID(String name) throws IllegalArgumentException {
return Command.valueOf(name).getID();
}
}
+ public static class MeterIdentification implements BaseCluster {
+ public static final long ID = 2822L;
+ public long getID() {
+ return ID;
+ }
+
+ public enum Attribute {
+ MeterType(0L),
+ PointOfDelivery(1L),
+ MeterSerialNumber(2L),
+ ProtocolVersion(3L),
+ PowerThreshold(4L),
+ GeneratedCommandList(65528L),
+ AcceptedCommandList(65529L),
+ EventList(65530L),
+ AttributeList(65531L),
+ FeatureMap(65532L),
+ ClusterRevision(65533L),;
+ private final long id;
+ Attribute(long id) {
+ this.id = id;
+ }
+
+ public long getID() {
+ return id;
+ }
+
+ public static Attribute value(long id) throws NoSuchFieldError {
+ for (Attribute attribute : Attribute.values()) {
+ if (attribute.getID() == id) {
+ return attribute;
+ }
+ }
+ throw new NoSuchFieldError();
+ }
+ }
+
+ public enum Event {;
+ private final long id;
+ Event(long id) {
+ this.id = id;
+ }
+
+ public long getID() {
+ return id;
+ }
+
+ public static Event value(long id) throws NoSuchFieldError {
+ for (Event event : Event.values()) {
+ if (event.getID() == id) {
+ return event;
+ }
+ }
+ throw new NoSuchFieldError();
+ }
+ }
+
+ public enum Command {;
+ private final long id;
+ Command(long id) {
+ this.id = id;
+ }
+
+ public long getID() {
+ return id;
+ }
+
+ public static Command value(long id) throws NoSuchFieldError {
+ for (Command command : Command.values()) {
+ if (command.getID() == id) {
+ return command;
+ }
+ }
+ throw new NoSuchFieldError();
+ }
+ }@Override
+ public String getAttributeName(long id) throws NoSuchFieldError {
+ return Attribute.value(id).toString();
+ }
+
+ @Override
+ public String getEventName(long id) throws NoSuchFieldError {
+ return Event.value(id).toString();
+ }
+
+ @Override
+ public String getCommandName(long id) throws NoSuchFieldError {
+ return Command.value(id).toString();
+ }
+
+ @Override
+ public long getAttributeID(String name) throws IllegalArgumentException {
+ return Attribute.valueOf(name).getID();
+ }
+
+ @Override
+ public long getEventID(String name) throws IllegalArgumentException {
+ return Event.valueOf(name).getID();
+ }
+
+ @Override
+ public long getCommandID(String name) throws IllegalArgumentException {
+ return Command.valueOf(name).getID();
+ }
+ }
public static class UnitTesting implements BaseCluster {
public static final long ID = 4294048773L;
public long getID() {
diff --git a/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java
index 7d6a3303c0aedf..74121ab118259f 100644
--- a/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java
+++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java
@@ -21857,6 +21857,195 @@ public void onError(Exception ex) {
}
}
+ public static class DelegatedMeterIdentificationClusterMeterTypeAttributeCallback implements ChipClusters.MeterIdentificationCluster.MeterTypeAttributeCallback, DelegatedClusterCallback {
+ private ClusterCommandCallback callback;
+ @Override
+ public void setCallbackDelegate(ClusterCommandCallback callback) {
+ this.callback = callback;
+ }
+
+ @Override
+ public void onSuccess(@Nullable Integer value) {
+ Map responseValues = new LinkedHashMap<>();
+ CommandResponseInfo commandResponseInfo = new CommandResponseInfo("value", "Integer");
+ responseValues.put(commandResponseInfo, value);
+ callback.onSuccess(responseValues);
+ }
+
+ @Override
+ public void onError(Exception ex) {
+ callback.onFailure(ex);
+ }
+ }
+
+ public static class DelegatedMeterIdentificationClusterPointOfDeliveryAttributeCallback implements ChipClusters.MeterIdentificationCluster.PointOfDeliveryAttributeCallback, DelegatedClusterCallback {
+ private ClusterCommandCallback callback;
+ @Override
+ public void setCallbackDelegate(ClusterCommandCallback callback) {
+ this.callback = callback;
+ }
+
+ @Override
+ public void onSuccess(@Nullable String value) {
+ Map responseValues = new LinkedHashMap<>();
+ CommandResponseInfo commandResponseInfo = new CommandResponseInfo("value", "String");
+ responseValues.put(commandResponseInfo, value);
+ callback.onSuccess(responseValues);
+ }
+
+ @Override
+ public void onError(Exception ex) {
+ callback.onFailure(ex);
+ }
+ }
+
+ public static class DelegatedMeterIdentificationClusterMeterSerialNumberAttributeCallback implements ChipClusters.MeterIdentificationCluster.MeterSerialNumberAttributeCallback, DelegatedClusterCallback {
+ private ClusterCommandCallback callback;
+ @Override
+ public void setCallbackDelegate(ClusterCommandCallback callback) {
+ this.callback = callback;
+ }
+
+ @Override
+ public void onSuccess(@Nullable String value) {
+ Map responseValues = new LinkedHashMap<>();
+ CommandResponseInfo commandResponseInfo = new CommandResponseInfo("value", "String");
+ responseValues.put(commandResponseInfo, value);
+ callback.onSuccess(responseValues);
+ }
+
+ @Override
+ public void onError(Exception ex) {
+ callback.onFailure(ex);
+ }
+ }
+
+ public static class DelegatedMeterIdentificationClusterProtocolVersionAttributeCallback implements ChipClusters.MeterIdentificationCluster.ProtocolVersionAttributeCallback, DelegatedClusterCallback {
+ private ClusterCommandCallback callback;
+ @Override
+ public void setCallbackDelegate(ClusterCommandCallback callback) {
+ this.callback = callback;
+ }
+
+ @Override
+ public void onSuccess(@Nullable String value) {
+ Map responseValues = new LinkedHashMap<>();
+ CommandResponseInfo commandResponseInfo = new CommandResponseInfo("value", "String");
+ responseValues.put(commandResponseInfo, value);
+ callback.onSuccess(responseValues);
+ }
+
+ @Override
+ public void onError(Exception ex) {
+ callback.onFailure(ex);
+ }
+ }
+
+ public static class DelegatedMeterIdentificationClusterPowerThresholdAttributeCallback implements ChipClusters.MeterIdentificationCluster.PowerThresholdAttributeCallback, DelegatedClusterCallback {
+ private ClusterCommandCallback callback;
+ @Override
+ public void setCallbackDelegate(ClusterCommandCallback callback) {
+ this.callback = callback;
+ }
+
+ @Override
+ public void onSuccess(@Nullable ChipStructs.MeterIdentificationClusterPowerThresholdStruct value) {
+ Map responseValues = new LinkedHashMap<>();
+ CommandResponseInfo commandResponseInfo = new CommandResponseInfo("value", "ChipStructs.MeterIdentificationClusterPowerThresholdStruct");
+ responseValues.put(commandResponseInfo, value);
+ callback.onSuccess(responseValues);
+ }
+
+ @Override
+ public void onError(Exception ex) {
+ callback.onFailure(ex);
+ }
+ }
+
+ public static class DelegatedMeterIdentificationClusterGeneratedCommandListAttributeCallback implements ChipClusters.MeterIdentificationCluster.GeneratedCommandListAttributeCallback, DelegatedClusterCallback {
+ private ClusterCommandCallback callback;
+ @Override
+ public void setCallbackDelegate(ClusterCommandCallback callback) {
+ this.callback = callback;
+ }
+
+ @Override
+ public void onSuccess(List valueList) {
+ Map responseValues = new LinkedHashMap<>();
+ CommandResponseInfo commandResponseInfo = new CommandResponseInfo("valueList", "List");
+ responseValues.put(commandResponseInfo, valueList);
+ callback.onSuccess(responseValues);
+ }
+
+ @Override
+ public void onError(Exception ex) {
+ callback.onFailure(ex);
+ }
+ }
+
+ public static class DelegatedMeterIdentificationClusterAcceptedCommandListAttributeCallback implements ChipClusters.MeterIdentificationCluster.AcceptedCommandListAttributeCallback, DelegatedClusterCallback {
+ private ClusterCommandCallback callback;
+ @Override
+ public void setCallbackDelegate(ClusterCommandCallback callback) {
+ this.callback = callback;
+ }
+
+ @Override
+ public void onSuccess(List valueList) {
+ Map responseValues = new LinkedHashMap<>();
+ CommandResponseInfo commandResponseInfo = new CommandResponseInfo("valueList", "List");
+ responseValues.put(commandResponseInfo, valueList);
+ callback.onSuccess(responseValues);
+ }
+
+ @Override
+ public void onError(Exception ex) {
+ callback.onFailure(ex);
+ }
+ }
+
+ public static class DelegatedMeterIdentificationClusterEventListAttributeCallback implements ChipClusters.MeterIdentificationCluster.EventListAttributeCallback, DelegatedClusterCallback {
+ private ClusterCommandCallback callback;
+ @Override
+ public void setCallbackDelegate(ClusterCommandCallback callback) {
+ this.callback = callback;
+ }
+
+ @Override
+ public void onSuccess(List valueList) {
+ Map responseValues = new LinkedHashMap<>();
+ CommandResponseInfo commandResponseInfo = new CommandResponseInfo("valueList", "List");
+ responseValues.put(commandResponseInfo, valueList);
+ callback.onSuccess(responseValues);
+ }
+
+ @Override
+ public void onError(Exception ex) {
+ callback.onFailure(ex);
+ }
+ }
+
+ public static class DelegatedMeterIdentificationClusterAttributeListAttributeCallback implements ChipClusters.MeterIdentificationCluster.AttributeListAttributeCallback, DelegatedClusterCallback {
+ private ClusterCommandCallback callback;
+ @Override
+ public void setCallbackDelegate(ClusterCommandCallback callback) {
+ this.callback = callback;
+ }
+
+ @Override
+ public void onSuccess(List valueList) {
+ Map responseValues = new LinkedHashMap<>();
+ CommandResponseInfo commandResponseInfo = new CommandResponseInfo("valueList", "List");
+ responseValues.put(commandResponseInfo, valueList);
+ callback.onSuccess(responseValues);
+ }
+
+ @Override
+ public void onError(Exception ex) {
+ callback.onFailure(ex);
+ }
+ }
+
public static class DelegatedUnitTestingClusterTestSpecificResponseCallback implements ChipClusters.UnitTestingCluster.TestSpecificResponseCallback, DelegatedClusterCallback {
private ClusterCommandCallback callback;
@@ -23997,6 +24186,10 @@ public Map initializeClusterMap() {
(ptr, endpointId) -> new ChipClusters.TlsCertificateManagementCluster(ptr, endpointId), new HashMap<>());
clusterMap.put("tlsCertificateManagement", tlsCertificateManagementClusterInfo);
+ ClusterInfo meterIdentificationClusterInfo = new ClusterInfo(
+ (ptr, endpointId) -> new ChipClusters.MeterIdentificationCluster(ptr, endpointId), new HashMap<>());
+ clusterMap.put("meterIdentification", meterIdentificationClusterInfo);
+
ClusterInfo unitTestingClusterInfo = new ClusterInfo(
(ptr, endpointId) -> new ChipClusters.UnitTestingCluster(ptr, endpointId), new HashMap<>());
clusterMap.put("unitTesting", unitTestingClusterInfo);
@@ -24141,6 +24334,7 @@ public void combineCommand(Map destination, Map> getCommandMap() {
commandMap.put("tlsCertificateManagement", tlsCertificateManagementClusterInteractionInfoMap);
+ Map meterIdentificationClusterInteractionInfoMap = new LinkedHashMap<>();
+
+ commandMap.put("meterIdentification", meterIdentificationClusterInteractionInfoMap);
+
Map unitTestingClusterInteractionInfoMap = new LinkedHashMap<>();
Map unitTestingtestCommandParams = new LinkedHashMap();
diff --git a/src/controller/java/generated/java/chip/devicecontroller/ClusterReadMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterReadMapping.java
index 0281673b384c4b..3392ffbb258e65 100644
--- a/src/controller/java/generated/java/chip/devicecontroller/ClusterReadMapping.java
+++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterReadMapping.java
@@ -19732,6 +19732,120 @@ private static Map readTlsCertificateManagementInteract
return result;
}
+ private static Map readMeterIdentificationInteractionInfo() {
+ Map result = new LinkedHashMap<>();Map readMeterIdentificationMeterTypeCommandParams = new LinkedHashMap();
+ InteractionInfo readMeterIdentificationMeterTypeAttributeInteractionInfo = new InteractionInfo(
+ (cluster, callback, commandArguments) -> {
+ ((ChipClusters.MeterIdentificationCluster) cluster).readMeterTypeAttribute(
+ (ChipClusters.MeterIdentificationCluster.MeterTypeAttributeCallback) callback
+ );
+ },
+ () -> new ClusterInfoMapping.DelegatedMeterIdentificationClusterMeterTypeAttributeCallback(),
+ readMeterIdentificationMeterTypeCommandParams
+ );
+ result.put("readMeterTypeAttribute", readMeterIdentificationMeterTypeAttributeInteractionInfo);
+ Map readMeterIdentificationPointOfDeliveryCommandParams = new LinkedHashMap();
+ InteractionInfo readMeterIdentificationPointOfDeliveryAttributeInteractionInfo = new InteractionInfo(
+ (cluster, callback, commandArguments) -> {
+ ((ChipClusters.MeterIdentificationCluster) cluster).readPointOfDeliveryAttribute(
+ (ChipClusters.MeterIdentificationCluster.PointOfDeliveryAttributeCallback) callback
+ );
+ },
+ () -> new ClusterInfoMapping.DelegatedMeterIdentificationClusterPointOfDeliveryAttributeCallback(),
+ readMeterIdentificationPointOfDeliveryCommandParams
+ );
+ result.put("readPointOfDeliveryAttribute", readMeterIdentificationPointOfDeliveryAttributeInteractionInfo);
+ Map readMeterIdentificationMeterSerialNumberCommandParams = new LinkedHashMap();
+ InteractionInfo readMeterIdentificationMeterSerialNumberAttributeInteractionInfo = new InteractionInfo(
+ (cluster, callback, commandArguments) -> {
+ ((ChipClusters.MeterIdentificationCluster) cluster).readMeterSerialNumberAttribute(
+ (ChipClusters.MeterIdentificationCluster.MeterSerialNumberAttributeCallback) callback
+ );
+ },
+ () -> new ClusterInfoMapping.DelegatedMeterIdentificationClusterMeterSerialNumberAttributeCallback(),
+ readMeterIdentificationMeterSerialNumberCommandParams
+ );
+ result.put("readMeterSerialNumberAttribute", readMeterIdentificationMeterSerialNumberAttributeInteractionInfo);
+ Map readMeterIdentificationProtocolVersionCommandParams = new LinkedHashMap();
+ InteractionInfo readMeterIdentificationProtocolVersionAttributeInteractionInfo = new InteractionInfo(
+ (cluster, callback, commandArguments) -> {
+ ((ChipClusters.MeterIdentificationCluster) cluster).readProtocolVersionAttribute(
+ (ChipClusters.MeterIdentificationCluster.ProtocolVersionAttributeCallback) callback
+ );
+ },
+ () -> new ClusterInfoMapping.DelegatedMeterIdentificationClusterProtocolVersionAttributeCallback(),
+ readMeterIdentificationProtocolVersionCommandParams
+ );
+ result.put("readProtocolVersionAttribute", readMeterIdentificationProtocolVersionAttributeInteractionInfo);
+ Map readMeterIdentificationGeneratedCommandListCommandParams = new LinkedHashMap();
+ InteractionInfo readMeterIdentificationGeneratedCommandListAttributeInteractionInfo = new InteractionInfo(
+ (cluster, callback, commandArguments) -> {
+ ((ChipClusters.MeterIdentificationCluster) cluster).readGeneratedCommandListAttribute(
+ (ChipClusters.MeterIdentificationCluster.GeneratedCommandListAttributeCallback) callback
+ );
+ },
+ () -> new ClusterInfoMapping.DelegatedMeterIdentificationClusterGeneratedCommandListAttributeCallback(),
+ readMeterIdentificationGeneratedCommandListCommandParams
+ );
+ result.put("readGeneratedCommandListAttribute", readMeterIdentificationGeneratedCommandListAttributeInteractionInfo);
+ Map readMeterIdentificationAcceptedCommandListCommandParams = new LinkedHashMap();
+ InteractionInfo readMeterIdentificationAcceptedCommandListAttributeInteractionInfo = new InteractionInfo(
+ (cluster, callback, commandArguments) -> {
+ ((ChipClusters.MeterIdentificationCluster) cluster).readAcceptedCommandListAttribute(
+ (ChipClusters.MeterIdentificationCluster.AcceptedCommandListAttributeCallback) callback
+ );
+ },
+ () -> new ClusterInfoMapping.DelegatedMeterIdentificationClusterAcceptedCommandListAttributeCallback(),
+ readMeterIdentificationAcceptedCommandListCommandParams
+ );
+ result.put("readAcceptedCommandListAttribute", readMeterIdentificationAcceptedCommandListAttributeInteractionInfo);
+ Map readMeterIdentificationEventListCommandParams = new LinkedHashMap();
+ InteractionInfo readMeterIdentificationEventListAttributeInteractionInfo = new InteractionInfo(
+ (cluster, callback, commandArguments) -> {
+ ((ChipClusters.MeterIdentificationCluster) cluster).readEventListAttribute(
+ (ChipClusters.MeterIdentificationCluster.EventListAttributeCallback) callback
+ );
+ },
+ () -> new ClusterInfoMapping.DelegatedMeterIdentificationClusterEventListAttributeCallback(),
+ readMeterIdentificationEventListCommandParams
+ );
+ result.put("readEventListAttribute", readMeterIdentificationEventListAttributeInteractionInfo);
+ Map readMeterIdentificationAttributeListCommandParams = new LinkedHashMap();
+ InteractionInfo readMeterIdentificationAttributeListAttributeInteractionInfo = new InteractionInfo(
+ (cluster, callback, commandArguments) -> {
+ ((ChipClusters.MeterIdentificationCluster) cluster).readAttributeListAttribute(
+ (ChipClusters.MeterIdentificationCluster.AttributeListAttributeCallback) callback
+ );
+ },
+ () -> new ClusterInfoMapping.DelegatedMeterIdentificationClusterAttributeListAttributeCallback(),
+ readMeterIdentificationAttributeListCommandParams
+ );
+ result.put("readAttributeListAttribute", readMeterIdentificationAttributeListAttributeInteractionInfo);
+ Map readMeterIdentificationFeatureMapCommandParams = new LinkedHashMap();
+ InteractionInfo readMeterIdentificationFeatureMapAttributeInteractionInfo = new InteractionInfo(
+ (cluster, callback, commandArguments) -> {
+ ((ChipClusters.MeterIdentificationCluster) cluster).readFeatureMapAttribute(
+ (ChipClusters.LongAttributeCallback) callback
+ );
+ },
+ () -> new ClusterInfoMapping.DelegatedLongAttributeCallback(),
+ readMeterIdentificationFeatureMapCommandParams
+ );
+ result.put("readFeatureMapAttribute", readMeterIdentificationFeatureMapAttributeInteractionInfo);
+ Map readMeterIdentificationClusterRevisionCommandParams = new LinkedHashMap();
+ InteractionInfo readMeterIdentificationClusterRevisionAttributeInteractionInfo = new InteractionInfo(
+ (cluster, callback, commandArguments) -> {
+ ((ChipClusters.MeterIdentificationCluster) cluster).readClusterRevisionAttribute(
+ (ChipClusters.IntegerAttributeCallback) callback
+ );
+ },
+ () -> new ClusterInfoMapping.DelegatedIntegerAttributeCallback(),
+ readMeterIdentificationClusterRevisionCommandParams
+ );
+ result.put("readClusterRevisionAttribute", readMeterIdentificationClusterRevisionAttributeInteractionInfo);
+
+ return result;
+ }
private static Map readUnitTestingInteractionInfo() {
Map result = new LinkedHashMap<>();Map readUnitTestingBooleanCommandParams = new LinkedHashMap();
InteractionInfo readUnitTestingBooleanAttributeInteractionInfo = new InteractionInfo(
@@ -21020,6 +21134,7 @@ public Map> getReadAttributeMap() {
put("ecosystemInformation", readEcosystemInformationInteractionInfo());
put("commissionerControl", readCommissionerControlInteractionInfo());
put("tlsCertificateManagement", readTlsCertificateManagementInteractionInfo());
+ put("meterIdentification", readMeterIdentificationInteractionInfo());
put("unitTesting", readUnitTestingInteractionInfo());
put("faultInjection", readFaultInjectionInteractionInfo());
put("sampleMei", readSampleMeiInteractionInfo());}};
diff --git a/src/controller/java/generated/java/chip/devicecontroller/ClusterWriteMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterWriteMapping.java
index 123f1da613439a..113e197d20d3f4 100644
--- a/src/controller/java/generated/java/chip/devicecontroller/ClusterWriteMapping.java
+++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterWriteMapping.java
@@ -3802,6 +3802,8 @@ public Map> getWriteAttributeMap() {
writeAttributeMap.put("commissionerControl", writeCommissionerControlInteractionInfo);
Map writeTlsCertificateManagementInteractionInfo = new LinkedHashMap<>();
writeAttributeMap.put("tlsCertificateManagement", writeTlsCertificateManagementInteractionInfo);
+ Map writeMeterIdentificationInteractionInfo = new LinkedHashMap<>();
+ writeAttributeMap.put("meterIdentification", writeMeterIdentificationInteractionInfo);
Map writeUnitTestingInteractionInfo = new LinkedHashMap<>();
Map writeUnitTestingBooleanCommandParams = new LinkedHashMap();
CommandParameterInfo unitTestingbooleanCommandParameterInfo =
diff --git a/src/controller/java/generated/java/chip/devicecontroller/cluster/files.gni b/src/controller/java/generated/java/chip/devicecontroller/cluster/files.gni
index 8c1864dd3cb333..47bce27e53e740 100644
--- a/src/controller/java/generated/java/chip/devicecontroller/cluster/files.gni
+++ b/src/controller/java/generated/java/chip/devicecontroller/cluster/files.gni
@@ -107,6 +107,7 @@ structs_sources = [
"${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/MediaPlaybackClusterTrackStruct.kt",
"${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/MessagesClusterMessageResponseOptionStruct.kt",
"${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/MessagesClusterMessageStruct.kt",
+ "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/MeterIdentificationClusterPowerThresholdStruct.kt",
"${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/MicrowaveOvenModeClusterModeOptionStruct.kt",
"${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/MicrowaveOvenModeClusterModeTagStruct.kt",
"${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/ModeSelectClusterModeOptionStruct.kt",
diff --git a/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/MeterIdentificationClusterPowerThresholdStruct.kt b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/MeterIdentificationClusterPowerThresholdStruct.kt
new file mode 100644
index 00000000000000..ebbd955ea4bd38
--- /dev/null
+++ b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/MeterIdentificationClusterPowerThresholdStruct.kt
@@ -0,0 +1,95 @@
+/*
+ *
+ * Copyright (c) 2023 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.
+ */
+package chip.devicecontroller.cluster.structs
+
+import chip.devicecontroller.cluster.*
+import java.util.Optional
+import matter.tlv.ContextSpecificTag
+import matter.tlv.Tag
+import matter.tlv.TlvReader
+import matter.tlv.TlvWriter
+
+class MeterIdentificationClusterPowerThresholdStruct(
+ val powerThreshold: Optional,
+ val apparentPowerThreshold: Optional,
+ val powerThresholdSource: UInt?,
+) {
+ override fun toString(): String = buildString {
+ append("MeterIdentificationClusterPowerThresholdStruct {\n")
+ append("\tpowerThreshold : $powerThreshold\n")
+ append("\tapparentPowerThreshold : $apparentPowerThreshold\n")
+ append("\tpowerThresholdSource : $powerThresholdSource\n")
+ append("}\n")
+ }
+
+ fun toTlv(tlvTag: Tag, tlvWriter: TlvWriter) {
+ tlvWriter.apply {
+ startStructure(tlvTag)
+ if (powerThreshold.isPresent) {
+ val optpowerThreshold = powerThreshold.get()
+ put(ContextSpecificTag(TAG_POWER_THRESHOLD), optpowerThreshold)
+ }
+ if (apparentPowerThreshold.isPresent) {
+ val optapparentPowerThreshold = apparentPowerThreshold.get()
+ put(ContextSpecificTag(TAG_APPARENT_POWER_THRESHOLD), optapparentPowerThreshold)
+ }
+ if (powerThresholdSource != null) {
+ put(ContextSpecificTag(TAG_POWER_THRESHOLD_SOURCE), powerThresholdSource)
+ } else {
+ putNull(ContextSpecificTag(TAG_POWER_THRESHOLD_SOURCE))
+ }
+ endStructure()
+ }
+ }
+
+ companion object {
+ private const val TAG_POWER_THRESHOLD = 0
+ private const val TAG_APPARENT_POWER_THRESHOLD = 1
+ private const val TAG_POWER_THRESHOLD_SOURCE = 2
+
+ fun fromTlv(tlvTag: Tag, tlvReader: TlvReader): MeterIdentificationClusterPowerThresholdStruct {
+ tlvReader.enterStructure(tlvTag)
+ val powerThreshold =
+ if (tlvReader.isNextTag(ContextSpecificTag(TAG_POWER_THRESHOLD))) {
+ Optional.of(tlvReader.getLong(ContextSpecificTag(TAG_POWER_THRESHOLD)))
+ } else {
+ Optional.empty()
+ }
+ val apparentPowerThreshold =
+ if (tlvReader.isNextTag(ContextSpecificTag(TAG_APPARENT_POWER_THRESHOLD))) {
+ Optional.of(tlvReader.getLong(ContextSpecificTag(TAG_APPARENT_POWER_THRESHOLD)))
+ } else {
+ Optional.empty()
+ }
+ val powerThresholdSource =
+ if (!tlvReader.isNull()) {
+ tlvReader.getUInt(ContextSpecificTag(TAG_POWER_THRESHOLD_SOURCE))
+ } else {
+ tlvReader.getNull(ContextSpecificTag(TAG_POWER_THRESHOLD_SOURCE))
+ null
+ }
+
+ tlvReader.exitContainer()
+
+ return MeterIdentificationClusterPowerThresholdStruct(
+ powerThreshold,
+ apparentPowerThreshold,
+ powerThresholdSource,
+ )
+ }
+ }
+}
diff --git a/src/controller/java/generated/java/matter/controller/cluster/clusters/MeterIdentificationCluster.kt b/src/controller/java/generated/java/matter/controller/cluster/clusters/MeterIdentificationCluster.kt
new file mode 100644
index 00000000000000..c0a0c581efb337
--- /dev/null
+++ b/src/controller/java/generated/java/matter/controller/cluster/clusters/MeterIdentificationCluster.kt
@@ -0,0 +1,1173 @@
+/*
+ *
+ * Copyright (c) 2023 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.
+ */
+
+package matter.controller.cluster.clusters
+
+import java.time.Duration
+import java.util.logging.Level
+import java.util.logging.Logger
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.transform
+import matter.controller.MatterController
+import matter.controller.ReadData
+import matter.controller.ReadRequest
+import matter.controller.SubscribeRequest
+import matter.controller.SubscriptionState
+import matter.controller.UIntSubscriptionState
+import matter.controller.UShortSubscriptionState
+import matter.controller.cluster.structs.*
+import matter.controller.model.AttributePath
+import matter.tlv.AnonymousTag
+import matter.tlv.TlvReader
+
+class MeterIdentificationCluster(
+ private val controller: MatterController,
+ private val endpointId: UShort,
+) {
+ class MeterTypeAttribute(val value: UByte?)
+
+ sealed class MeterTypeAttributeSubscriptionState {
+ data class Success(val value: UByte?) : MeterTypeAttributeSubscriptionState()
+
+ data class Error(val exception: Exception) : MeterTypeAttributeSubscriptionState()
+
+ object SubscriptionEstablished : MeterTypeAttributeSubscriptionState()
+ }
+
+ class PointOfDeliveryAttribute(val value: String?)
+
+ sealed class PointOfDeliveryAttributeSubscriptionState {
+ data class Success(val value: String?) : PointOfDeliveryAttributeSubscriptionState()
+
+ data class Error(val exception: Exception) : PointOfDeliveryAttributeSubscriptionState()
+
+ object SubscriptionEstablished : PointOfDeliveryAttributeSubscriptionState()
+ }
+
+ class MeterSerialNumberAttribute(val value: String?)
+
+ sealed class MeterSerialNumberAttributeSubscriptionState {
+ data class Success(val value: String?) : MeterSerialNumberAttributeSubscriptionState()
+
+ data class Error(val exception: Exception) : MeterSerialNumberAttributeSubscriptionState()
+
+ object SubscriptionEstablished : MeterSerialNumberAttributeSubscriptionState()
+ }
+
+ class ProtocolVersionAttribute(val value: String?)
+
+ sealed class ProtocolVersionAttributeSubscriptionState {
+ data class Success(val value: String?) : ProtocolVersionAttributeSubscriptionState()
+
+ data class Error(val exception: Exception) : ProtocolVersionAttributeSubscriptionState()
+
+ object SubscriptionEstablished : ProtocolVersionAttributeSubscriptionState()
+ }
+
+ class PowerThresholdAttribute(val value: MeterIdentificationClusterPowerThresholdStruct?)
+
+ sealed class PowerThresholdAttributeSubscriptionState {
+ data class Success(val value: MeterIdentificationClusterPowerThresholdStruct?) :
+ PowerThresholdAttributeSubscriptionState()
+
+ data class Error(val exception: Exception) : PowerThresholdAttributeSubscriptionState()
+
+ object SubscriptionEstablished : PowerThresholdAttributeSubscriptionState()
+ }
+
+ class GeneratedCommandListAttribute(val value: List)
+
+ sealed class GeneratedCommandListAttributeSubscriptionState {
+ data class Success(val value: List) : GeneratedCommandListAttributeSubscriptionState()
+
+ data class Error(val exception: Exception) : GeneratedCommandListAttributeSubscriptionState()
+
+ object SubscriptionEstablished : GeneratedCommandListAttributeSubscriptionState()
+ }
+
+ class AcceptedCommandListAttribute(val value: List)
+
+ sealed class AcceptedCommandListAttributeSubscriptionState {
+ data class Success(val value: List) : AcceptedCommandListAttributeSubscriptionState()
+
+ data class Error(val exception: Exception) : AcceptedCommandListAttributeSubscriptionState()
+
+ object SubscriptionEstablished : AcceptedCommandListAttributeSubscriptionState()
+ }
+
+ class EventListAttribute(val value: List)
+
+ sealed class EventListAttributeSubscriptionState {
+ data class Success(val value: List) : EventListAttributeSubscriptionState()
+
+ data class Error(val exception: Exception) : EventListAttributeSubscriptionState()
+
+ object SubscriptionEstablished : EventListAttributeSubscriptionState()
+ }
+
+ class AttributeListAttribute(val value: List)
+
+ sealed class AttributeListAttributeSubscriptionState {
+ data class Success(val value: List) : AttributeListAttributeSubscriptionState()
+
+ data class Error(val exception: Exception) : AttributeListAttributeSubscriptionState()
+
+ object SubscriptionEstablished : AttributeListAttributeSubscriptionState()
+ }
+
+ suspend fun readMeterTypeAttribute(): MeterTypeAttribute {
+ val ATTRIBUTE_ID: UInt = 0u
+
+ val attributePath =
+ AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID)
+
+ val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath))
+
+ val response = controller.read(readRequest)
+
+ if (response.successes.isEmpty()) {
+ logger.log(Level.WARNING, "Read command failed")
+ throw IllegalStateException("Read command failed with failures: ${response.failures}")
+ }
+
+ logger.log(Level.FINE, "Read command succeeded")
+
+ val attributeData =
+ response.successes.filterIsInstance().firstOrNull {
+ it.path.attributeId == ATTRIBUTE_ID
+ }
+
+ requireNotNull(attributeData) { "Metertype attribute not found in response" }
+
+ // Decode the TLV data into the appropriate type
+ val tlvReader = TlvReader(attributeData.data)
+ val decodedValue: UByte? =
+ if (!tlvReader.isNull()) {
+ tlvReader.getUByte(AnonymousTag)
+ } else {
+ tlvReader.getNull(AnonymousTag)
+ null
+ }
+
+ return MeterTypeAttribute(decodedValue)
+ }
+
+ suspend fun subscribeMeterTypeAttribute(
+ minInterval: Int,
+ maxInterval: Int,
+ ): Flow {
+ val ATTRIBUTE_ID: UInt = 0u
+ val attributePaths =
+ listOf(
+ AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID)
+ )
+
+ val subscribeRequest: SubscribeRequest =
+ SubscribeRequest(
+ eventPaths = emptyList(),
+ attributePaths = attributePaths,
+ minInterval = Duration.ofSeconds(minInterval.toLong()),
+ maxInterval = Duration.ofSeconds(maxInterval.toLong()),
+ )
+
+ return controller.subscribe(subscribeRequest).transform { subscriptionState ->
+ when (subscriptionState) {
+ is SubscriptionState.SubscriptionErrorNotification -> {
+ emit(
+ MeterTypeAttributeSubscriptionState.Error(
+ Exception(
+ "Subscription terminated with error code: ${subscriptionState.terminationCause}"
+ )
+ )
+ )
+ }
+ is SubscriptionState.NodeStateUpdate -> {
+ val attributeData =
+ subscriptionState.updateState.successes
+ .filterIsInstance()
+ .firstOrNull { it.path.attributeId == ATTRIBUTE_ID }
+
+ requireNotNull(attributeData) { "Metertype attribute not found in Node State update" }
+
+ // Decode the TLV data into the appropriate type
+ val tlvReader = TlvReader(attributeData.data)
+ val decodedValue: UByte? =
+ if (!tlvReader.isNull()) {
+ tlvReader.getUByte(AnonymousTag)
+ } else {
+ tlvReader.getNull(AnonymousTag)
+ null
+ }
+
+ decodedValue?.let { emit(MeterTypeAttributeSubscriptionState.Success(it)) }
+ }
+ SubscriptionState.SubscriptionEstablished -> {
+ emit(MeterTypeAttributeSubscriptionState.SubscriptionEstablished)
+ }
+ }
+ }
+ }
+
+ suspend fun readPointOfDeliveryAttribute(): PointOfDeliveryAttribute {
+ val ATTRIBUTE_ID: UInt = 1u
+
+ val attributePath =
+ AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID)
+
+ val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath))
+
+ val response = controller.read(readRequest)
+
+ if (response.successes.isEmpty()) {
+ logger.log(Level.WARNING, "Read command failed")
+ throw IllegalStateException("Read command failed with failures: ${response.failures}")
+ }
+
+ logger.log(Level.FINE, "Read command succeeded")
+
+ val attributeData =
+ response.successes.filterIsInstance().firstOrNull {
+ it.path.attributeId == ATTRIBUTE_ID
+ }
+
+ requireNotNull(attributeData) { "Pointofdelivery attribute not found in response" }
+
+ // Decode the TLV data into the appropriate type
+ val tlvReader = TlvReader(attributeData.data)
+ val decodedValue: String? =
+ if (!tlvReader.isNull()) {
+ tlvReader.getString(AnonymousTag)
+ } else {
+ tlvReader.getNull(AnonymousTag)
+ null
+ }
+
+ return PointOfDeliveryAttribute(decodedValue)
+ }
+
+ suspend fun subscribePointOfDeliveryAttribute(
+ minInterval: Int,
+ maxInterval: Int,
+ ): Flow {
+ val ATTRIBUTE_ID: UInt = 1u
+ val attributePaths =
+ listOf(
+ AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID)
+ )
+
+ val subscribeRequest: SubscribeRequest =
+ SubscribeRequest(
+ eventPaths = emptyList(),
+ attributePaths = attributePaths,
+ minInterval = Duration.ofSeconds(minInterval.toLong()),
+ maxInterval = Duration.ofSeconds(maxInterval.toLong()),
+ )
+
+ return controller.subscribe(subscribeRequest).transform { subscriptionState ->
+ when (subscriptionState) {
+ is SubscriptionState.SubscriptionErrorNotification -> {
+ emit(
+ PointOfDeliveryAttributeSubscriptionState.Error(
+ Exception(
+ "Subscription terminated with error code: ${subscriptionState.terminationCause}"
+ )
+ )
+ )
+ }
+ is SubscriptionState.NodeStateUpdate -> {
+ val attributeData =
+ subscriptionState.updateState.successes
+ .filterIsInstance()
+ .firstOrNull { it.path.attributeId == ATTRIBUTE_ID }
+
+ requireNotNull(attributeData) {
+ "Pointofdelivery attribute not found in Node State update"
+ }
+
+ // Decode the TLV data into the appropriate type
+ val tlvReader = TlvReader(attributeData.data)
+ val decodedValue: String? =
+ if (!tlvReader.isNull()) {
+ tlvReader.getString(AnonymousTag)
+ } else {
+ tlvReader.getNull(AnonymousTag)
+ null
+ }
+
+ decodedValue?.let { emit(PointOfDeliveryAttributeSubscriptionState.Success(it)) }
+ }
+ SubscriptionState.SubscriptionEstablished -> {
+ emit(PointOfDeliveryAttributeSubscriptionState.SubscriptionEstablished)
+ }
+ }
+ }
+ }
+
+ suspend fun readMeterSerialNumberAttribute(): MeterSerialNumberAttribute {
+ val ATTRIBUTE_ID: UInt = 2u
+
+ val attributePath =
+ AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID)
+
+ val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath))
+
+ val response = controller.read(readRequest)
+
+ if (response.successes.isEmpty()) {
+ logger.log(Level.WARNING, "Read command failed")
+ throw IllegalStateException("Read command failed with failures: ${response.failures}")
+ }
+
+ logger.log(Level.FINE, "Read command succeeded")
+
+ val attributeData =
+ response.successes.filterIsInstance().firstOrNull {
+ it.path.attributeId == ATTRIBUTE_ID
+ }
+
+ requireNotNull(attributeData) { "Meterserialnumber attribute not found in response" }
+
+ // Decode the TLV data into the appropriate type
+ val tlvReader = TlvReader(attributeData.data)
+ val decodedValue: String? =
+ if (!tlvReader.isNull()) {
+ tlvReader.getString(AnonymousTag)
+ } else {
+ tlvReader.getNull(AnonymousTag)
+ null
+ }
+
+ return MeterSerialNumberAttribute(decodedValue)
+ }
+
+ suspend fun subscribeMeterSerialNumberAttribute(
+ minInterval: Int,
+ maxInterval: Int,
+ ): Flow {
+ val ATTRIBUTE_ID: UInt = 2u
+ val attributePaths =
+ listOf(
+ AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID)
+ )
+
+ val subscribeRequest: SubscribeRequest =
+ SubscribeRequest(
+ eventPaths = emptyList(),
+ attributePaths = attributePaths,
+ minInterval = Duration.ofSeconds(minInterval.toLong()),
+ maxInterval = Duration.ofSeconds(maxInterval.toLong()),
+ )
+
+ return controller.subscribe(subscribeRequest).transform { subscriptionState ->
+ when (subscriptionState) {
+ is SubscriptionState.SubscriptionErrorNotification -> {
+ emit(
+ MeterSerialNumberAttributeSubscriptionState.Error(
+ Exception(
+ "Subscription terminated with error code: ${subscriptionState.terminationCause}"
+ )
+ )
+ )
+ }
+ is SubscriptionState.NodeStateUpdate -> {
+ val attributeData =
+ subscriptionState.updateState.successes
+ .filterIsInstance()
+ .firstOrNull { it.path.attributeId == ATTRIBUTE_ID }
+
+ requireNotNull(attributeData) {
+ "Meterserialnumber attribute not found in Node State update"
+ }
+
+ // Decode the TLV data into the appropriate type
+ val tlvReader = TlvReader(attributeData.data)
+ val decodedValue: String? =
+ if (!tlvReader.isNull()) {
+ tlvReader.getString(AnonymousTag)
+ } else {
+ tlvReader.getNull(AnonymousTag)
+ null
+ }
+
+ decodedValue?.let { emit(MeterSerialNumberAttributeSubscriptionState.Success(it)) }
+ }
+ SubscriptionState.SubscriptionEstablished -> {
+ emit(MeterSerialNumberAttributeSubscriptionState.SubscriptionEstablished)
+ }
+ }
+ }
+ }
+
+ suspend fun readProtocolVersionAttribute(): ProtocolVersionAttribute {
+ val ATTRIBUTE_ID: UInt = 3u
+
+ val attributePath =
+ AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID)
+
+ val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath))
+
+ val response = controller.read(readRequest)
+
+ if (response.successes.isEmpty()) {
+ logger.log(Level.WARNING, "Read command failed")
+ throw IllegalStateException("Read command failed with failures: ${response.failures}")
+ }
+
+ logger.log(Level.FINE, "Read command succeeded")
+
+ val attributeData =
+ response.successes.filterIsInstance().firstOrNull {
+ it.path.attributeId == ATTRIBUTE_ID
+ }
+
+ requireNotNull(attributeData) { "Protocolversion attribute not found in response" }
+
+ // Decode the TLV data into the appropriate type
+ val tlvReader = TlvReader(attributeData.data)
+ val decodedValue: String? =
+ if (!tlvReader.isNull()) {
+ if (tlvReader.isNextTag(AnonymousTag)) {
+ tlvReader.getString(AnonymousTag)
+ } else {
+ null
+ }
+ } else {
+ tlvReader.getNull(AnonymousTag)
+ null
+ }
+
+ return ProtocolVersionAttribute(decodedValue)
+ }
+
+ suspend fun subscribeProtocolVersionAttribute(
+ minInterval: Int,
+ maxInterval: Int,
+ ): Flow {
+ val ATTRIBUTE_ID: UInt = 3u
+ val attributePaths =
+ listOf(
+ AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID)
+ )
+
+ val subscribeRequest: SubscribeRequest =
+ SubscribeRequest(
+ eventPaths = emptyList(),
+ attributePaths = attributePaths,
+ minInterval = Duration.ofSeconds(minInterval.toLong()),
+ maxInterval = Duration.ofSeconds(maxInterval.toLong()),
+ )
+
+ return controller.subscribe(subscribeRequest).transform { subscriptionState ->
+ when (subscriptionState) {
+ is SubscriptionState.SubscriptionErrorNotification -> {
+ emit(
+ ProtocolVersionAttributeSubscriptionState.Error(
+ Exception(
+ "Subscription terminated with error code: ${subscriptionState.terminationCause}"
+ )
+ )
+ )
+ }
+ is SubscriptionState.NodeStateUpdate -> {
+ val attributeData =
+ subscriptionState.updateState.successes
+ .filterIsInstance()
+ .firstOrNull { it.path.attributeId == ATTRIBUTE_ID }
+
+ requireNotNull(attributeData) {
+ "Protocolversion attribute not found in Node State update"
+ }
+
+ // Decode the TLV data into the appropriate type
+ val tlvReader = TlvReader(attributeData.data)
+ val decodedValue: String? =
+ if (!tlvReader.isNull()) {
+ if (tlvReader.isNextTag(AnonymousTag)) {
+ tlvReader.getString(AnonymousTag)
+ } else {
+ null
+ }
+ } else {
+ tlvReader.getNull(AnonymousTag)
+ null
+ }
+
+ decodedValue?.let { emit(ProtocolVersionAttributeSubscriptionState.Success(it)) }
+ }
+ SubscriptionState.SubscriptionEstablished -> {
+ emit(ProtocolVersionAttributeSubscriptionState.SubscriptionEstablished)
+ }
+ }
+ }
+ }
+
+ suspend fun readPowerThresholdAttribute(): PowerThresholdAttribute {
+ val ATTRIBUTE_ID: UInt = 4u
+
+ val attributePath =
+ AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID)
+
+ val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath))
+
+ val response = controller.read(readRequest)
+
+ if (response.successes.isEmpty()) {
+ logger.log(Level.WARNING, "Read command failed")
+ throw IllegalStateException("Read command failed with failures: ${response.failures}")
+ }
+
+ logger.log(Level.FINE, "Read command succeeded")
+
+ val attributeData =
+ response.successes.filterIsInstance().firstOrNull {
+ it.path.attributeId == ATTRIBUTE_ID
+ }
+
+ requireNotNull(attributeData) { "Powerthreshold attribute not found in response" }
+
+ // Decode the TLV data into the appropriate type
+ val tlvReader = TlvReader(attributeData.data)
+ val decodedValue: MeterIdentificationClusterPowerThresholdStruct? =
+ if (!tlvReader.isNull()) {
+ if (tlvReader.isNextTag(AnonymousTag)) {
+ MeterIdentificationClusterPowerThresholdStruct.fromTlv(AnonymousTag, tlvReader)
+ } else {
+ null
+ }
+ } else {
+ tlvReader.getNull(AnonymousTag)
+ null
+ }
+
+ return PowerThresholdAttribute(decodedValue)
+ }
+
+ suspend fun subscribePowerThresholdAttribute(
+ minInterval: Int,
+ maxInterval: Int,
+ ): Flow {
+ val ATTRIBUTE_ID: UInt = 4u
+ val attributePaths =
+ listOf(
+ AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID)
+ )
+
+ val subscribeRequest: SubscribeRequest =
+ SubscribeRequest(
+ eventPaths = emptyList(),
+ attributePaths = attributePaths,
+ minInterval = Duration.ofSeconds(minInterval.toLong()),
+ maxInterval = Duration.ofSeconds(maxInterval.toLong()),
+ )
+
+ return controller.subscribe(subscribeRequest).transform { subscriptionState ->
+ when (subscriptionState) {
+ is SubscriptionState.SubscriptionErrorNotification -> {
+ emit(
+ PowerThresholdAttributeSubscriptionState.Error(
+ Exception(
+ "Subscription terminated with error code: ${subscriptionState.terminationCause}"
+ )
+ )
+ )
+ }
+ is SubscriptionState.NodeStateUpdate -> {
+ val attributeData =
+ subscriptionState.updateState.successes
+ .filterIsInstance()
+ .firstOrNull { it.path.attributeId == ATTRIBUTE_ID }
+
+ requireNotNull(attributeData) {
+ "Powerthreshold attribute not found in Node State update"
+ }
+
+ // Decode the TLV data into the appropriate type
+ val tlvReader = TlvReader(attributeData.data)
+ val decodedValue: MeterIdentificationClusterPowerThresholdStruct? =
+ if (!tlvReader.isNull()) {
+ if (tlvReader.isNextTag(AnonymousTag)) {
+ MeterIdentificationClusterPowerThresholdStruct.fromTlv(AnonymousTag, tlvReader)
+ } else {
+ null
+ }
+ } else {
+ tlvReader.getNull(AnonymousTag)
+ null
+ }
+
+ decodedValue?.let { emit(PowerThresholdAttributeSubscriptionState.Success(it)) }
+ }
+ SubscriptionState.SubscriptionEstablished -> {
+ emit(PowerThresholdAttributeSubscriptionState.SubscriptionEstablished)
+ }
+ }
+ }
+ }
+
+ suspend fun readGeneratedCommandListAttribute(): GeneratedCommandListAttribute {
+ val ATTRIBUTE_ID: UInt = 65528u
+
+ val attributePath =
+ AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID)
+
+ val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath))
+
+ val response = controller.read(readRequest)
+
+ if (response.successes.isEmpty()) {
+ logger.log(Level.WARNING, "Read command failed")
+ throw IllegalStateException("Read command failed with failures: ${response.failures}")
+ }
+
+ logger.log(Level.FINE, "Read command succeeded")
+
+ val attributeData =
+ response.successes.filterIsInstance().firstOrNull {
+ it.path.attributeId == ATTRIBUTE_ID
+ }
+
+ requireNotNull(attributeData) { "Generatedcommandlist attribute not found in response" }
+
+ // Decode the TLV data into the appropriate type
+ val tlvReader = TlvReader(attributeData.data)
+ val decodedValue: List =
+ buildList {
+ tlvReader.enterArray(AnonymousTag)
+ while (!tlvReader.isEndOfContainer()) {
+ add(tlvReader.getUInt(AnonymousTag))
+ }
+ tlvReader.exitContainer()
+ }
+
+ return GeneratedCommandListAttribute(decodedValue)
+ }
+
+ suspend fun subscribeGeneratedCommandListAttribute(
+ minInterval: Int,
+ maxInterval: Int,
+ ): Flow {
+ val ATTRIBUTE_ID: UInt = 65528u
+ val attributePaths =
+ listOf(
+ AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID)
+ )
+
+ val subscribeRequest: SubscribeRequest =
+ SubscribeRequest(
+ eventPaths = emptyList(),
+ attributePaths = attributePaths,
+ minInterval = Duration.ofSeconds(minInterval.toLong()),
+ maxInterval = Duration.ofSeconds(maxInterval.toLong()),
+ )
+
+ return controller.subscribe(subscribeRequest).transform { subscriptionState ->
+ when (subscriptionState) {
+ is SubscriptionState.SubscriptionErrorNotification -> {
+ emit(
+ GeneratedCommandListAttributeSubscriptionState.Error(
+ Exception(
+ "Subscription terminated with error code: ${subscriptionState.terminationCause}"
+ )
+ )
+ )
+ }
+ is SubscriptionState.NodeStateUpdate -> {
+ val attributeData =
+ subscriptionState.updateState.successes
+ .filterIsInstance()
+ .firstOrNull { it.path.attributeId == ATTRIBUTE_ID }
+
+ requireNotNull(attributeData) {
+ "Generatedcommandlist attribute not found in Node State update"
+ }
+
+ // Decode the TLV data into the appropriate type
+ val tlvReader = TlvReader(attributeData.data)
+ val decodedValue: List =
+ buildList {
+ tlvReader.enterArray(AnonymousTag)
+ while (!tlvReader.isEndOfContainer()) {
+ add(tlvReader.getUInt(AnonymousTag))
+ }
+ tlvReader.exitContainer()
+ }
+
+ emit(GeneratedCommandListAttributeSubscriptionState.Success(decodedValue))
+ }
+ SubscriptionState.SubscriptionEstablished -> {
+ emit(GeneratedCommandListAttributeSubscriptionState.SubscriptionEstablished)
+ }
+ }
+ }
+ }
+
+ suspend fun readAcceptedCommandListAttribute(): AcceptedCommandListAttribute {
+ val ATTRIBUTE_ID: UInt = 65529u
+
+ val attributePath =
+ AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID)
+
+ val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath))
+
+ val response = controller.read(readRequest)
+
+ if (response.successes.isEmpty()) {
+ logger.log(Level.WARNING, "Read command failed")
+ throw IllegalStateException("Read command failed with failures: ${response.failures}")
+ }
+
+ logger.log(Level.FINE, "Read command succeeded")
+
+ val attributeData =
+ response.successes.filterIsInstance().firstOrNull {
+ it.path.attributeId == ATTRIBUTE_ID
+ }
+
+ requireNotNull(attributeData) { "Acceptedcommandlist attribute not found in response" }
+
+ // Decode the TLV data into the appropriate type
+ val tlvReader = TlvReader(attributeData.data)
+ val decodedValue: List =
+ buildList {
+ tlvReader.enterArray(AnonymousTag)
+ while (!tlvReader.isEndOfContainer()) {
+ add(tlvReader.getUInt(AnonymousTag))
+ }
+ tlvReader.exitContainer()
+ }
+
+ return AcceptedCommandListAttribute(decodedValue)
+ }
+
+ suspend fun subscribeAcceptedCommandListAttribute(
+ minInterval: Int,
+ maxInterval: Int,
+ ): Flow {
+ val ATTRIBUTE_ID: UInt = 65529u
+ val attributePaths =
+ listOf(
+ AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID)
+ )
+
+ val subscribeRequest: SubscribeRequest =
+ SubscribeRequest(
+ eventPaths = emptyList(),
+ attributePaths = attributePaths,
+ minInterval = Duration.ofSeconds(minInterval.toLong()),
+ maxInterval = Duration.ofSeconds(maxInterval.toLong()),
+ )
+
+ return controller.subscribe(subscribeRequest).transform { subscriptionState ->
+ when (subscriptionState) {
+ is SubscriptionState.SubscriptionErrorNotification -> {
+ emit(
+ AcceptedCommandListAttributeSubscriptionState.Error(
+ Exception(
+ "Subscription terminated with error code: ${subscriptionState.terminationCause}"
+ )
+ )
+ )
+ }
+ is SubscriptionState.NodeStateUpdate -> {
+ val attributeData =
+ subscriptionState.updateState.successes
+ .filterIsInstance()
+ .firstOrNull { it.path.attributeId == ATTRIBUTE_ID }
+
+ requireNotNull(attributeData) {
+ "Acceptedcommandlist attribute not found in Node State update"
+ }
+
+ // Decode the TLV data into the appropriate type
+ val tlvReader = TlvReader(attributeData.data)
+ val decodedValue: List =
+ buildList {
+ tlvReader.enterArray(AnonymousTag)
+ while (!tlvReader.isEndOfContainer()) {
+ add(tlvReader.getUInt(AnonymousTag))
+ }
+ tlvReader.exitContainer()
+ }
+
+ emit(AcceptedCommandListAttributeSubscriptionState.Success(decodedValue))
+ }
+ SubscriptionState.SubscriptionEstablished -> {
+ emit(AcceptedCommandListAttributeSubscriptionState.SubscriptionEstablished)
+ }
+ }
+ }
+ }
+
+ suspend fun readEventListAttribute(): EventListAttribute {
+ val ATTRIBUTE_ID: UInt = 65530u
+
+ val attributePath =
+ AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID)
+
+ val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath))
+
+ val response = controller.read(readRequest)
+
+ if (response.successes.isEmpty()) {
+ logger.log(Level.WARNING, "Read command failed")
+ throw IllegalStateException("Read command failed with failures: ${response.failures}")
+ }
+
+ logger.log(Level.FINE, "Read command succeeded")
+
+ val attributeData =
+ response.successes.filterIsInstance().firstOrNull {
+ it.path.attributeId == ATTRIBUTE_ID
+ }
+
+ requireNotNull(attributeData) { "Eventlist attribute not found in response" }
+
+ // Decode the TLV data into the appropriate type
+ val tlvReader = TlvReader(attributeData.data)
+ val decodedValue: List =
+ buildList {
+ tlvReader.enterArray(AnonymousTag)
+ while (!tlvReader.isEndOfContainer()) {
+ add(tlvReader.getUInt(AnonymousTag))
+ }
+ tlvReader.exitContainer()
+ }
+
+ return EventListAttribute(decodedValue)
+ }
+
+ suspend fun subscribeEventListAttribute(
+ minInterval: Int,
+ maxInterval: Int,
+ ): Flow {
+ val ATTRIBUTE_ID: UInt = 65530u
+ val attributePaths =
+ listOf(
+ AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID)
+ )
+
+ val subscribeRequest: SubscribeRequest =
+ SubscribeRequest(
+ eventPaths = emptyList(),
+ attributePaths = attributePaths,
+ minInterval = Duration.ofSeconds(minInterval.toLong()),
+ maxInterval = Duration.ofSeconds(maxInterval.toLong()),
+ )
+
+ return controller.subscribe(subscribeRequest).transform { subscriptionState ->
+ when (subscriptionState) {
+ is SubscriptionState.SubscriptionErrorNotification -> {
+ emit(
+ EventListAttributeSubscriptionState.Error(
+ Exception(
+ "Subscription terminated with error code: ${subscriptionState.terminationCause}"
+ )
+ )
+ )
+ }
+ is SubscriptionState.NodeStateUpdate -> {
+ val attributeData =
+ subscriptionState.updateState.successes
+ .filterIsInstance()
+ .firstOrNull { it.path.attributeId == ATTRIBUTE_ID }
+
+ requireNotNull(attributeData) { "Eventlist attribute not found in Node State update" }
+
+ // Decode the TLV data into the appropriate type
+ val tlvReader = TlvReader(attributeData.data)
+ val decodedValue: List =
+ buildList {
+ tlvReader.enterArray(AnonymousTag)
+ while (!tlvReader.isEndOfContainer()) {
+ add(tlvReader.getUInt(AnonymousTag))
+ }
+ tlvReader.exitContainer()
+ }
+
+ emit(EventListAttributeSubscriptionState.Success(decodedValue))
+ }
+ SubscriptionState.SubscriptionEstablished -> {
+ emit(EventListAttributeSubscriptionState.SubscriptionEstablished)
+ }
+ }
+ }
+ }
+
+ suspend fun readAttributeListAttribute(): AttributeListAttribute {
+ val ATTRIBUTE_ID: UInt = 65531u
+
+ val attributePath =
+ AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID)
+
+ val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath))
+
+ val response = controller.read(readRequest)
+
+ if (response.successes.isEmpty()) {
+ logger.log(Level.WARNING, "Read command failed")
+ throw IllegalStateException("Read command failed with failures: ${response.failures}")
+ }
+
+ logger.log(Level.FINE, "Read command succeeded")
+
+ val attributeData =
+ response.successes.filterIsInstance().firstOrNull {
+ it.path.attributeId == ATTRIBUTE_ID
+ }
+
+ requireNotNull(attributeData) { "Attributelist attribute not found in response" }
+
+ // Decode the TLV data into the appropriate type
+ val tlvReader = TlvReader(attributeData.data)
+ val decodedValue: List =
+ buildList {
+ tlvReader.enterArray(AnonymousTag)
+ while (!tlvReader.isEndOfContainer()) {
+ add(tlvReader.getUInt(AnonymousTag))
+ }
+ tlvReader.exitContainer()
+ }
+
+ return AttributeListAttribute(decodedValue)
+ }
+
+ suspend fun subscribeAttributeListAttribute(
+ minInterval: Int,
+ maxInterval: Int,
+ ): Flow {
+ val ATTRIBUTE_ID: UInt = 65531u
+ val attributePaths =
+ listOf(
+ AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID)
+ )
+
+ val subscribeRequest: SubscribeRequest =
+ SubscribeRequest(
+ eventPaths = emptyList(),
+ attributePaths = attributePaths,
+ minInterval = Duration.ofSeconds(minInterval.toLong()),
+ maxInterval = Duration.ofSeconds(maxInterval.toLong()),
+ )
+
+ return controller.subscribe(subscribeRequest).transform { subscriptionState ->
+ when (subscriptionState) {
+ is SubscriptionState.SubscriptionErrorNotification -> {
+ emit(
+ AttributeListAttributeSubscriptionState.Error(
+ Exception(
+ "Subscription terminated with error code: ${subscriptionState.terminationCause}"
+ )
+ )
+ )
+ }
+ is SubscriptionState.NodeStateUpdate -> {
+ val attributeData =
+ subscriptionState.updateState.successes
+ .filterIsInstance()
+ .firstOrNull { it.path.attributeId == ATTRIBUTE_ID }
+
+ requireNotNull(attributeData) { "Attributelist attribute not found in Node State update" }
+
+ // Decode the TLV data into the appropriate type
+ val tlvReader = TlvReader(attributeData.data)
+ val decodedValue: List =
+ buildList {
+ tlvReader.enterArray(AnonymousTag)
+ while (!tlvReader.isEndOfContainer()) {
+ add(tlvReader.getUInt(AnonymousTag))
+ }
+ tlvReader.exitContainer()
+ }
+
+ emit(AttributeListAttributeSubscriptionState.Success(decodedValue))
+ }
+ SubscriptionState.SubscriptionEstablished -> {
+ emit(AttributeListAttributeSubscriptionState.SubscriptionEstablished)
+ }
+ }
+ }
+ }
+
+ suspend fun readFeatureMapAttribute(): UInt {
+ val ATTRIBUTE_ID: UInt = 65532u
+
+ val attributePath =
+ AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID)
+
+ val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath))
+
+ val response = controller.read(readRequest)
+
+ if (response.successes.isEmpty()) {
+ logger.log(Level.WARNING, "Read command failed")
+ throw IllegalStateException("Read command failed with failures: ${response.failures}")
+ }
+
+ logger.log(Level.FINE, "Read command succeeded")
+
+ val attributeData =
+ response.successes.filterIsInstance().firstOrNull {
+ it.path.attributeId == ATTRIBUTE_ID
+ }
+
+ requireNotNull(attributeData) { "Featuremap attribute not found in response" }
+
+ // Decode the TLV data into the appropriate type
+ val tlvReader = TlvReader(attributeData.data)
+ val decodedValue: UInt = tlvReader.getUInt(AnonymousTag)
+
+ return decodedValue
+ }
+
+ suspend fun subscribeFeatureMapAttribute(
+ minInterval: Int,
+ maxInterval: Int,
+ ): Flow {
+ val ATTRIBUTE_ID: UInt = 65532u
+ val attributePaths =
+ listOf(
+ AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID)
+ )
+
+ val subscribeRequest: SubscribeRequest =
+ SubscribeRequest(
+ eventPaths = emptyList(),
+ attributePaths = attributePaths,
+ minInterval = Duration.ofSeconds(minInterval.toLong()),
+ maxInterval = Duration.ofSeconds(maxInterval.toLong()),
+ )
+
+ return controller.subscribe(subscribeRequest).transform { subscriptionState ->
+ when (subscriptionState) {
+ is SubscriptionState.SubscriptionErrorNotification -> {
+ emit(
+ UIntSubscriptionState.Error(
+ Exception(
+ "Subscription terminated with error code: ${subscriptionState.terminationCause}"
+ )
+ )
+ )
+ }
+ is SubscriptionState.NodeStateUpdate -> {
+ val attributeData =
+ subscriptionState.updateState.successes
+ .filterIsInstance()
+ .firstOrNull { it.path.attributeId == ATTRIBUTE_ID }
+
+ requireNotNull(attributeData) { "Featuremap attribute not found in Node State update" }
+
+ // Decode the TLV data into the appropriate type
+ val tlvReader = TlvReader(attributeData.data)
+ val decodedValue: UInt = tlvReader.getUInt(AnonymousTag)
+
+ emit(UIntSubscriptionState.Success(decodedValue))
+ }
+ SubscriptionState.SubscriptionEstablished -> {
+ emit(UIntSubscriptionState.SubscriptionEstablished)
+ }
+ }
+ }
+ }
+
+ suspend fun readClusterRevisionAttribute(): UShort {
+ val ATTRIBUTE_ID: UInt = 65533u
+
+ val attributePath =
+ AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID)
+
+ val readRequest = ReadRequest(eventPaths = emptyList(), attributePaths = listOf(attributePath))
+
+ val response = controller.read(readRequest)
+
+ if (response.successes.isEmpty()) {
+ logger.log(Level.WARNING, "Read command failed")
+ throw IllegalStateException("Read command failed with failures: ${response.failures}")
+ }
+
+ logger.log(Level.FINE, "Read command succeeded")
+
+ val attributeData =
+ response.successes.filterIsInstance().firstOrNull {
+ it.path.attributeId == ATTRIBUTE_ID
+ }
+
+ requireNotNull(attributeData) { "Clusterrevision attribute not found in response" }
+
+ // Decode the TLV data into the appropriate type
+ val tlvReader = TlvReader(attributeData.data)
+ val decodedValue: UShort = tlvReader.getUShort(AnonymousTag)
+
+ return decodedValue
+ }
+
+ suspend fun subscribeClusterRevisionAttribute(
+ minInterval: Int,
+ maxInterval: Int,
+ ): Flow {
+ val ATTRIBUTE_ID: UInt = 65533u
+ val attributePaths =
+ listOf(
+ AttributePath(endpointId = endpointId, clusterId = CLUSTER_ID, attributeId = ATTRIBUTE_ID)
+ )
+
+ val subscribeRequest: SubscribeRequest =
+ SubscribeRequest(
+ eventPaths = emptyList(),
+ attributePaths = attributePaths,
+ minInterval = Duration.ofSeconds(minInterval.toLong()),
+ maxInterval = Duration.ofSeconds(maxInterval.toLong()),
+ )
+
+ return controller.subscribe(subscribeRequest).transform { subscriptionState ->
+ when (subscriptionState) {
+ is SubscriptionState.SubscriptionErrorNotification -> {
+ emit(
+ UShortSubscriptionState.Error(
+ Exception(
+ "Subscription terminated with error code: ${subscriptionState.terminationCause}"
+ )
+ )
+ )
+ }
+ is SubscriptionState.NodeStateUpdate -> {
+ val attributeData =
+ subscriptionState.updateState.successes
+ .filterIsInstance()
+ .firstOrNull { it.path.attributeId == ATTRIBUTE_ID }
+
+ requireNotNull(attributeData) {
+ "Clusterrevision attribute not found in Node State update"
+ }
+
+ // Decode the TLV data into the appropriate type
+ val tlvReader = TlvReader(attributeData.data)
+ val decodedValue: UShort = tlvReader.getUShort(AnonymousTag)
+
+ emit(UShortSubscriptionState.Success(decodedValue))
+ }
+ SubscriptionState.SubscriptionEstablished -> {
+ emit(UShortSubscriptionState.SubscriptionEstablished)
+ }
+ }
+ }
+ }
+
+ companion object {
+ private val logger = Logger.getLogger(MeterIdentificationCluster::class.java.name)
+ const val CLUSTER_ID: UInt = 2822u
+ }
+}
diff --git a/src/controller/java/generated/java/matter/controller/cluster/files.gni b/src/controller/java/generated/java/matter/controller/cluster/files.gni
index e25dcf66937659..412baa73a49403 100644
--- a/src/controller/java/generated/java/matter/controller/cluster/files.gni
+++ b/src/controller/java/generated/java/matter/controller/cluster/files.gni
@@ -107,6 +107,7 @@ matter_structs_sources = [
"${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/MediaPlaybackClusterTrackStruct.kt",
"${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/MessagesClusterMessageResponseOptionStruct.kt",
"${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/MessagesClusterMessageStruct.kt",
+ "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/MeterIdentificationClusterPowerThresholdStruct.kt",
"${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/MicrowaveOvenModeClusterModeOptionStruct.kt",
"${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/MicrowaveOvenModeClusterModeTagStruct.kt",
"${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/ModeSelectClusterModeOptionStruct.kt",
@@ -351,6 +352,7 @@ matter_clusters_sources = [
"${chip_root}/src/controller/java/generated/java/matter/controller/cluster/clusters/MediaInputCluster.kt",
"${chip_root}/src/controller/java/generated/java/matter/controller/cluster/clusters/MediaPlaybackCluster.kt",
"${chip_root}/src/controller/java/generated/java/matter/controller/cluster/clusters/MessagesCluster.kt",
+ "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/clusters/MeterIdentificationCluster.kt",
"${chip_root}/src/controller/java/generated/java/matter/controller/cluster/clusters/MicrowaveOvenControlCluster.kt",
"${chip_root}/src/controller/java/generated/java/matter/controller/cluster/clusters/MicrowaveOvenModeCluster.kt",
"${chip_root}/src/controller/java/generated/java/matter/controller/cluster/clusters/ModeSelectCluster.kt",
diff --git a/src/controller/java/generated/java/matter/controller/cluster/structs/MeterIdentificationClusterPowerThresholdStruct.kt b/src/controller/java/generated/java/matter/controller/cluster/structs/MeterIdentificationClusterPowerThresholdStruct.kt
new file mode 100644
index 00000000000000..9e061839039320
--- /dev/null
+++ b/src/controller/java/generated/java/matter/controller/cluster/structs/MeterIdentificationClusterPowerThresholdStruct.kt
@@ -0,0 +1,95 @@
+/*
+ *
+ * Copyright (c) 2023 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.
+ */
+package matter.controller.cluster.structs
+
+import java.util.Optional
+import matter.controller.cluster.*
+import matter.tlv.ContextSpecificTag
+import matter.tlv.Tag
+import matter.tlv.TlvReader
+import matter.tlv.TlvWriter
+
+class MeterIdentificationClusterPowerThresholdStruct(
+ val powerThreshold: Optional,
+ val apparentPowerThreshold: Optional,
+ val powerThresholdSource: UByte?,
+) {
+ override fun toString(): String = buildString {
+ append("MeterIdentificationClusterPowerThresholdStruct {\n")
+ append("\tpowerThreshold : $powerThreshold\n")
+ append("\tapparentPowerThreshold : $apparentPowerThreshold\n")
+ append("\tpowerThresholdSource : $powerThresholdSource\n")
+ append("}\n")
+ }
+
+ fun toTlv(tlvTag: Tag, tlvWriter: TlvWriter) {
+ tlvWriter.apply {
+ startStructure(tlvTag)
+ if (powerThreshold.isPresent) {
+ val optpowerThreshold = powerThreshold.get()
+ put(ContextSpecificTag(TAG_POWER_THRESHOLD), optpowerThreshold)
+ }
+ if (apparentPowerThreshold.isPresent) {
+ val optapparentPowerThreshold = apparentPowerThreshold.get()
+ put(ContextSpecificTag(TAG_APPARENT_POWER_THRESHOLD), optapparentPowerThreshold)
+ }
+ if (powerThresholdSource != null) {
+ put(ContextSpecificTag(TAG_POWER_THRESHOLD_SOURCE), powerThresholdSource)
+ } else {
+ putNull(ContextSpecificTag(TAG_POWER_THRESHOLD_SOURCE))
+ }
+ endStructure()
+ }
+ }
+
+ companion object {
+ private const val TAG_POWER_THRESHOLD = 0
+ private const val TAG_APPARENT_POWER_THRESHOLD = 1
+ private const val TAG_POWER_THRESHOLD_SOURCE = 2
+
+ fun fromTlv(tlvTag: Tag, tlvReader: TlvReader): MeterIdentificationClusterPowerThresholdStruct {
+ tlvReader.enterStructure(tlvTag)
+ val powerThreshold =
+ if (tlvReader.isNextTag(ContextSpecificTag(TAG_POWER_THRESHOLD))) {
+ Optional.of(tlvReader.getLong(ContextSpecificTag(TAG_POWER_THRESHOLD)))
+ } else {
+ Optional.empty()
+ }
+ val apparentPowerThreshold =
+ if (tlvReader.isNextTag(ContextSpecificTag(TAG_APPARENT_POWER_THRESHOLD))) {
+ Optional.of(tlvReader.getLong(ContextSpecificTag(TAG_APPARENT_POWER_THRESHOLD)))
+ } else {
+ Optional.empty()
+ }
+ val powerThresholdSource =
+ if (!tlvReader.isNull()) {
+ tlvReader.getUByte(ContextSpecificTag(TAG_POWER_THRESHOLD_SOURCE))
+ } else {
+ tlvReader.getNull(ContextSpecificTag(TAG_POWER_THRESHOLD_SOURCE))
+ null
+ }
+
+ tlvReader.exitContainer()
+
+ return MeterIdentificationClusterPowerThresholdStruct(
+ powerThreshold,
+ apparentPowerThreshold,
+ powerThresholdSource,
+ )
+ }
+ }
+}
diff --git a/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp b/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp
index 40bf333909e943..7bfc58807654c1 100644
--- a/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp
+++ b/src/controller/java/zap-generated/CHIPAttributeTLVValueDecoder.cpp
@@ -44118,6 +44118,296 @@ jobject DecodeAttributeValue(const app::ConcreteAttributePath & aPath, TLV::TLVR
}
break;
}
+ case app::Clusters::MeterIdentification::Id: {
+ using namespace app::Clusters::MeterIdentification;
+ switch (aPath.mAttributeId)
+ {
+ case Attributes::MeterType::Id: {
+ using TypeInfo = Attributes::MeterType::TypeInfo;
+ TypeInfo::DecodableType cppValue;
+ *aError = app::DataModel::Decode(aReader, cppValue);
+ if (*aError != CHIP_NO_ERROR)
+ {
+ return nullptr;
+ }
+ jobject value;
+ if (cppValue.IsNull())
+ {
+ value = nullptr;
+ }
+ else
+ {
+ std::string valueClassName = "java/lang/Integer";
+ std::string valueCtorSignature = "(I)V";
+ jint jnivalue = static_cast(cppValue.Value());
+ chip::JniReferences::GetInstance().CreateBoxedObject(valueClassName.c_str(), valueCtorSignature.c_str(),
+ jnivalue, value);
+ }
+ return value;
+ }
+ case Attributes::PointOfDelivery::Id: {
+ using TypeInfo = Attributes::PointOfDelivery::TypeInfo;
+ TypeInfo::DecodableType cppValue;
+ *aError = app::DataModel::Decode(aReader, cppValue);
+ if (*aError != CHIP_NO_ERROR)
+ {
+ return nullptr;
+ }
+ jobject value;
+ if (cppValue.IsNull())
+ {
+ value = nullptr;
+ }
+ else
+ {
+ LogErrorOnFailure(chip::JniReferences::GetInstance().CharToStringUTF(cppValue.Value(), value));
+ }
+ return value;
+ }
+ case Attributes::MeterSerialNumber::Id: {
+ using TypeInfo = Attributes::MeterSerialNumber::TypeInfo;
+ TypeInfo::DecodableType cppValue;
+ *aError = app::DataModel::Decode(aReader, cppValue);
+ if (*aError != CHIP_NO_ERROR)
+ {
+ return nullptr;
+ }
+ jobject value;
+ if (cppValue.IsNull())
+ {
+ value = nullptr;
+ }
+ else
+ {
+ LogErrorOnFailure(chip::JniReferences::GetInstance().CharToStringUTF(cppValue.Value(), value));
+ }
+ return value;
+ }
+ case Attributes::ProtocolVersion::Id: {
+ using TypeInfo = Attributes::ProtocolVersion::TypeInfo;
+ TypeInfo::DecodableType cppValue;
+ *aError = app::DataModel::Decode(aReader, cppValue);
+ if (*aError != CHIP_NO_ERROR)
+ {
+ return nullptr;
+ }
+ jobject value;
+ if (cppValue.IsNull())
+ {
+ value = nullptr;
+ }
+ else
+ {
+ LogErrorOnFailure(chip::JniReferences::GetInstance().CharToStringUTF(cppValue.Value(), value));
+ }
+ return value;
+ }
+ case Attributes::PowerThreshold::Id: {
+ using TypeInfo = Attributes::PowerThreshold::TypeInfo;
+ TypeInfo::DecodableType cppValue;
+ *aError = app::DataModel::Decode(aReader, cppValue);
+ if (*aError != CHIP_NO_ERROR)
+ {
+ return nullptr;
+ }
+ jobject value;
+ if (cppValue.IsNull())
+ {
+ value = nullptr;
+ }
+ else
+ {
+ jobject value_powerThreshold;
+ if (!cppValue.Value().powerThreshold.HasValue())
+ {
+ chip::JniReferences::GetInstance().CreateOptional(nullptr, value_powerThreshold);
+ }
+ else
+ {
+ jobject value_powerThresholdInsideOptional;
+ std::string value_powerThresholdInsideOptionalClassName = "java/lang/Long";
+ std::string value_powerThresholdInsideOptionalCtorSignature = "(J)V";
+ jlong jnivalue_powerThresholdInsideOptional = static_cast(cppValue.Value().powerThreshold.Value());
+ chip::JniReferences::GetInstance().CreateBoxedObject(
+ value_powerThresholdInsideOptionalClassName.c_str(),
+ value_powerThresholdInsideOptionalCtorSignature.c_str(), jnivalue_powerThresholdInsideOptional,
+ value_powerThresholdInsideOptional);
+ chip::JniReferences::GetInstance().CreateOptional(value_powerThresholdInsideOptional, value_powerThreshold);
+ }
+ jobject value_apparentPowerThreshold;
+ if (!cppValue.Value().apparentPowerThreshold.HasValue())
+ {
+ chip::JniReferences::GetInstance().CreateOptional(nullptr, value_apparentPowerThreshold);
+ }
+ else
+ {
+ jobject value_apparentPowerThresholdInsideOptional;
+ std::string value_apparentPowerThresholdInsideOptionalClassName = "java/lang/Long";
+ std::string value_apparentPowerThresholdInsideOptionalCtorSignature = "(J)V";
+ jlong jnivalue_apparentPowerThresholdInsideOptional =
+ static_cast(cppValue.Value().apparentPowerThreshold.Value());
+ chip::JniReferences::GetInstance().CreateBoxedObject(
+ value_apparentPowerThresholdInsideOptionalClassName.c_str(),
+ value_apparentPowerThresholdInsideOptionalCtorSignature.c_str(),
+ jnivalue_apparentPowerThresholdInsideOptional, value_apparentPowerThresholdInsideOptional);
+ chip::JniReferences::GetInstance().CreateOptional(value_apparentPowerThresholdInsideOptional,
+ value_apparentPowerThreshold);
+ }
+ jobject value_powerThresholdSource;
+ if (cppValue.Value().powerThresholdSource.IsNull())
+ {
+ value_powerThresholdSource = nullptr;
+ }
+ else
+ {
+ std::string value_powerThresholdSourceClassName = "java/lang/Integer";
+ std::string value_powerThresholdSourceCtorSignature = "(I)V";
+ jint jnivalue_powerThresholdSource = static_cast(cppValue.Value().powerThresholdSource.Value());
+ chip::JniReferences::GetInstance().CreateBoxedObject