blob: 8be16208169de26dca3f2f964ff3131152324824 [file] [log] [blame]
#
# 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.
import logging
import chip.clusters as Clusters
from matter_testing_support import MatterBaseTest, TestStep, async_test_body, default_matter_test_main
from mobly import asserts
from TC_EnergyReporting_Utils import EnergyReportingBaseTestHelper
logger = logging.getLogger(__name__)
MIN_INT64_ALLOWED = -pow(2, 62) # -(2^62)
MAX_INT64_ALLOWED = pow(2, 62) # (2^62)
class TC_EPM_2_1(MatterBaseTest, EnergyReportingBaseTestHelper):
def desc_TC_EPM_2_1(self) -> str:
"""Returns a description of this test"""
return "5.1.2. [TC-EPM-2.1] Attributes with Server as DUT"
def pics_TC_EPM_2_1(self):
""" This function returns a list of PICS for this test case that must be True for the test to be run"""
return ["EPM.S"]
def steps_TC_EPM_2_1(self) -> list[TestStep]:
steps = [
TestStep("1", "Commissioning, already done", is_commissioning=True),
TestStep("2", "TH reads PowerMode attribute. Verify that the DUT response contains an enum8 value"),
TestStep("3", "TH reads NumberOfMeasurementTypes attribute. Verify that the DUT response contains an uint8 value."),
TestStep("4", "TH reads Accuracy attribute. Verify that the DUT response contains a list of MeasurementAccuracyStruct entries - Verify that the list has between 1 and NumberOfMeasurementTypes entries."),
TestStep("5", "TH reads Ranges attribute. Verify that the DUT response contains a list of MeasurementRangeStruct entries - Verify that the list has between 0 and NumberOfMeasurementTypes entries."),
TestStep("6", "TH reads Voltage attribute. Verify that the DUT response contains either null or an int64 value. Value has to be between a range of -2^62 to 2^62."),
TestStep("7", "TH reads ActiveCurrent attribute. Verify that the DUT response contains either null or an int64 value. Value has to be between a range of -2^62 to 2^62."),
TestStep("8", "TH reads ReactiveCurrent attribute. Verify that the DUT response contains either null or an int64 value. Value has to be between a range of -2^62 to 2^62."),
TestStep("9", "TH reads ApparentCurrent attribute. Verify that the DUT response contains either null or an int64 value. Value has to be between a range of 0 to 2^62."),
TestStep("10", "TH reads ActivePower attribute. Verify that the DUT response contains either null or an int64 value. Value has to be between a range of -2^62 to 2^62."),
TestStep("11", "TH reads ReactivePower attribute. Verify that the DUT response contains either null or an int64 value. Value has to be between a range of -2^62 to 2^62."),
TestStep("12", "TH reads ApparentPower attribute. Verify that the DUT response contains either null or an int64 value. Value has to be between a range of -2^62 to 2^62."),
TestStep("13", "TH reads RMSVoltage attribute. Verify that the DUT response contains either null or an int64 value. Value has to be between a range of -2^62 to 2^62."),
TestStep("14", "TH reads RMSCurrent attribute. Verify that the DUT response contains either null or an int64 value. Value has to be between a range of -2^62 to 2^62."),
TestStep("15", "TH reads RMSPower attribute. Verify that the DUT response contains either null or an int64 value. Value has to be between a range of -2^62 to 2^62."),
TestStep("16", "TH reads Frequency attribute. Verify that the DUT response contains either null or an int64 value. Value has to be between a range of 0 to 1000000."),
TestStep("17", "TH reads HarmonicCurrents attribute. Verify that the DUT response contains a list of HarmonicMeasurementStruct entries."),
TestStep("18", "TH reads HarmonicPhases attribute. Verify that the DUT response contains a list of HarmonicMeasurementStruct entries."),
TestStep("19", "TH reads PowerFactor attribute. Verify that the DUT response contains either null or an int64 value. Value has to be between a range of -10000 to 10000."),
TestStep("20", "TH reads NeutralCurrent attribute. Verify that the DUT response contains either null or an int64 value. Value has to be between a range of -2^62 to 2^62."),
]
return steps
@async_test_body
async def test_TC_EPM_2_1(self):
self.step("1")
# Commission DUT - already done
supported_attributes = await self.get_supported_epm_attributes()
self.step("2")
power_mode = await self.read_epm_attribute_expect_success("PowerMode")
logger.info(f"Rx'd PowerMode: {power_mode}")
asserts.assert_not_equal(power_mode, Clusters.ElectricalPowerMeasurement.Enums.PowerModeEnum.kUnknown,
"PowerMode must not be Unknown")
self.step("3")
number_of_measurements = await self.read_epm_attribute_expect_success("NumberOfMeasurementTypes")
logger.info(f"Rx'd NumberOfMeasurementTypes: {number_of_measurements}")
asserts.assert_greater_equal(number_of_measurements, 1,
"NumberOfMeasurementTypes must be >= 1")
self.step("4")
accuracy = await self.read_epm_attribute_expect_success("Accuracy")
logger.info(f"Rx'd Accuracy: {accuracy}")
logger.info("Checking Accuracy meets spec requirements")
found_active_power = False
for measurement in accuracy:
logging.info(
f"measurementType:{measurement.measurementType} measured:{measurement.measured} minMeasuredValue:{measurement.minMeasuredValue} maxMeasuredValue:{measurement.maxMeasuredValue}")
# Scan all measurement types to check we have the mandatory kActivePower
if (measurement.measurementType == Clusters.ElectricalPowerMeasurement.Enums.MeasurementTypeEnum.kActivePower):
found_active_power = True
# Check that the ranges are in order from minimum to maximum and don't have gaps
asserts.assert_equal(measurement.minMeasuredValue, measurement.accuracyRanges[0].rangeMin,
"minMeasuredValue must be the same as 1st accuracyRange rangeMin")
for index, range_entry in enumerate(measurement.accuracyRanges):
logging.info(f" [{index}] rangeMin:{range_entry.rangeMin} rangeMax:{range_entry.rangeMax} percentMax:{range_entry.percentMax} percentMin:{range_entry.percentMin} percentTypical:{range_entry.percentTypical} fixedMax:{range_entry.fixedMax} fixedMin:{range_entry.fixedMin} fixedTypical:{range_entry.fixedTypical}")
asserts.assert_greater(range_entry.rangeMax, range_entry.rangeMin, "rangeMax should be > rangeMin")
if index == 0:
minimum_range = range_entry.rangeMin
maximum_range = range_entry.rangeMax
prev_range_max = range_entry.rangeMax
else:
minimum_range = min(minimum_range, range_entry.rangeMin)
maximum_range = max(maximum_range, range_entry.rangeMax)
asserts.assert_equal(range_entry.rangeMin, prev_range_max + 1,
f"Index[{index}] rangeMin was not +1 more then previous index's rangeMax {prev_range_max}")
prev_range_max = range_entry.rangeMax
# Check that the last range rangeMax has the same value as the measurement.maxMeasuredValue
asserts.assert_equal(measurement.maxMeasuredValue, prev_range_max,
"maxMeasuredValue must be the same as the last accuracyRange rangeMax")
asserts.assert_equal(maximum_range, measurement.maxMeasuredValue,
"The maxMeasuredValue must be the same as any of the maximum of all rangeMax's")
asserts.assert_equal(minimum_range, measurement.minMeasuredValue,
"The minMeasuredValue must be the same as any of the minimum of all rangeMin's")
asserts.assert_is(found_active_power, True, "There must be an ActivePower measurement accuracy")
asserts.assert_equal(len(accuracy), number_of_measurements,
"The number of accuracy entries should match the NumberOfMeasurementTypes")
self.step("5")
if self.pics_guard(Clusters.ElectricalPowerMeasurement.Attributes.Ranges.attribute_id in supported_attributes):
ranges = await self.read_epm_attribute_expect_success("Ranges")
logger.info(f"Rx'd Ranges: {ranges}")
# Check list length between 0 and NumberOfMeasurementTypes
asserts.assert_greater_equal(len(ranges), 0)
asserts.assert_less_equal(len(ranges), number_of_measurements)
self.step("6")
if self.pics_guard(Clusters.ElectricalPowerMeasurement.Attributes.Voltage.attribute_id in supported_attributes):
voltage = await self.check_epm_attribute_in_range("Voltage", MIN_INT64_ALLOWED, MAX_INT64_ALLOWED, allow_null=True)
logger.info(f"Rx'd Voltage: {voltage}")
self.step("7")
if self.pics_guard(Clusters.ElectricalPowerMeasurement.Attributes.ActiveCurrent.attribute_id in supported_attributes):
active_current = await self.check_epm_attribute_in_range("ActiveCurrent", MIN_INT64_ALLOWED, MAX_INT64_ALLOWED, allow_null=True)
logger.info(f"Rx'd ActiveCurrent: {active_current}")
self.step("8")
if self.pics_guard(Clusters.ElectricalPowerMeasurement.Attributes.ReactiveCurrent.attribute_id in supported_attributes):
reactive_current = await self.check_epm_attribute_in_range("ReactiveCurrent", MIN_INT64_ALLOWED, MAX_INT64_ALLOWED, allow_null=True)
logger.info(f"Rx'd ReactiveCurrent: {reactive_current}")
self.step("9")
if self.pics_guard(Clusters.ElectricalPowerMeasurement.Attributes.ApparentCurrent.attribute_id in supported_attributes):
apparent_current = await self.check_epm_attribute_in_range("ApparentCurrent", 0, MAX_INT64_ALLOWED, allow_null=True)
logger.info(f"Rx'd ApparentCurrent: {apparent_current}")
self.step("10")
if self.pics_guard(Clusters.ElectricalPowerMeasurement.Attributes.ActivePower.attribute_id in supported_attributes):
active_power = await self.check_epm_attribute_in_range("ActivePower", MIN_INT64_ALLOWED, MAX_INT64_ALLOWED, allow_null=True)
logger.info(f"Rx'd ActivePower: {active_power}")
self.step("11")
if self.pics_guard(Clusters.ElectricalPowerMeasurement.Attributes.ReactivePower.attribute_id in supported_attributes):
reactive_power = await self.check_epm_attribute_in_range("ReactivePower", MIN_INT64_ALLOWED, MAX_INT64_ALLOWED, allow_null=True)
logger.info(f"Rx'd ReactivePower: {reactive_power}")
self.step("12")
if self.pics_guard(Clusters.ElectricalPowerMeasurement.Attributes.ApparentPower.attribute_id in supported_attributes):
apparent_power = await self.check_epm_attribute_in_range("ApparentPower", MIN_INT64_ALLOWED, MAX_INT64_ALLOWED, allow_null=True)
logger.info(f"Rx'd ApparentPower: {apparent_power}")
self.step("13")
if self.pics_guard(Clusters.ElectricalPowerMeasurement.Attributes.RMSVoltage.attribute_id in supported_attributes):
rms_voltage = await self.check_epm_attribute_in_range("RMSVoltage", MIN_INT64_ALLOWED, MAX_INT64_ALLOWED, allow_null=True)
logger.info(f"Rx'd RMSVoltage: {rms_voltage}")
self.step("14")
if self.pics_guard(Clusters.ElectricalPowerMeasurement.Attributes.RMSCurrent.attribute_id in supported_attributes):
rms_current = await self.check_epm_attribute_in_range("RMSCurrent", MIN_INT64_ALLOWED, MAX_INT64_ALLOWED, allow_null=True)
logger.info(f"Rx'd RMSCurrent: {rms_current}")
self.step("15")
if self.pics_guard(Clusters.ElectricalPowerMeasurement.Attributes.RMSPower.attribute_id in supported_attributes):
rms_power = await self.check_epm_attribute_in_range("RMSPower", MIN_INT64_ALLOWED, MAX_INT64_ALLOWED, allow_null=True)
logger.info(f"Rx'd RMSPower: {rms_power}")
self.step("16")
if self.pics_guard(Clusters.ElectricalPowerMeasurement.Attributes.Frequency.attribute_id in supported_attributes):
frequency = await self.check_epm_attribute_in_range("Frequency", 0, 1000000, allow_null=True)
logger.info(f"Rx'd Frequency: {frequency}")
self.step("17")
if self.pics_guard(Clusters.ElectricalPowerMeasurement.Attributes.HarmonicCurrents.attribute_id in supported_attributes):
harmonic_currents = await self.read_epm_attribute_expect_success("HarmonicCurrents")
logger.info(f"Rx'd HarmonicCurrents: {harmonic_currents}")
asserts.assert_is(type(harmonic_currents), list)
for index, entry in enumerate(harmonic_currents):
logging.info(f" [{index}] order:{entry.order} measurement:{entry.measurement}")
asserts.assert_greater_equal(entry.order, 1)
self.check_value_in_range("Measurement", entry.measurement, MIN_INT64_ALLOWED, MAX_INT64_ALLOWED)
self.step("18")
if self.pics_guard(Clusters.ElectricalPowerMeasurement.Attributes.HarmonicPhases.attribute_id in supported_attributes):
harmonic_phases = await self.read_epm_attribute_expect_success("HarmonicPhases")
logger.info(f"Rx'd HarmonicPhases: {harmonic_phases}")
asserts.assert_is(type(harmonic_phases), list)
for index, entry in enumerate(harmonic_phases):
logging.info(f" [{index}] order:{entry.order} measurement:{entry.measurement}")
asserts.assert_greater_equal(entry.order, 1)
self.check_value_in_range("Measurement", entry.measurement, MIN_INT64_ALLOWED, MAX_INT64_ALLOWED)
self.step("19")
if self.pics_guard(Clusters.ElectricalPowerMeasurement.Attributes.PowerFactor.attribute_id in supported_attributes):
power_factor = await self.check_epm_attribute_in_range("PowerFactor", -10000, 10000, allow_null=True)
logger.info(f"Rx'd PowerFactor: {power_factor}")
self.step("20")
if self.pics_guard(Clusters.ElectricalPowerMeasurement.Attributes.NeutralCurrent.attribute_id in supported_attributes):
neutral_current = await self.check_epm_attribute_in_range("NeutralCurrent", MIN_INT64_ALLOWED, MAX_INT64_ALLOWED, allow_null=True)
logger.info(f"Rx'd NeutralCurrent: {neutral_current}")
if __name__ == "__main__":
default_matter_test_main()