|
| 1 | +# |
| 2 | +# Copyright (c) 2023 Project CHIP Authors |
| 3 | +# All rights reserved. |
| 4 | +# |
| 5 | +# Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | +# you may not use this file except in compliance with the License. |
| 7 | +# You may obtain a copy of the License at |
| 8 | +# |
| 9 | +# http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | +# |
| 11 | +# Unless required by applicable law or agreed to in writing, software |
| 12 | +# distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | +# See the License for the specific language governing permissions and |
| 15 | +# limitations under the License. |
| 16 | + |
| 17 | + |
| 18 | +import logging |
| 19 | + |
| 20 | +import chip.clusters as Clusters |
| 21 | +from chip.interaction_model import InteractionModelError, Status |
| 22 | +from matter_testing_support import utc_time_in_matter_epoch |
| 23 | +from mobly import asserts |
| 24 | + |
| 25 | +logger = logging.getLogger(__name__) |
| 26 | + |
| 27 | + |
| 28 | +class DEMTestBase: |
| 29 | + |
| 30 | + async def read_dem_attribute_expect_success(self, endpoint: int = None, attribute: str = ""): |
| 31 | + cluster = Clusters.Objects.DeviceEnergyManagement |
| 32 | + full_attr = getattr(cluster.Attributes, attribute) |
| 33 | + logging.info(f"endpoint {endpoint} full_attr {full_attr}") |
| 34 | + return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=full_attr) |
| 35 | + |
| 36 | + async def check_dem_attribute(self, attribute, expected_value, endpoint: int = None): |
| 37 | + value = await self.read_dem_attribute_expect_success(endpoint=endpoint, attribute=attribute) |
| 38 | + asserts.assert_equal(value, expected_value, |
| 39 | + f"Unexpected '{attribute}' value - expected {expected_value}, was {value}") |
| 40 | + |
| 41 | + async def send_power_adjustment_command(self, power: int, duration: int, |
| 42 | + cause: Clusters.Objects.DeviceEnergyManagement.Enums.CauseEnum, |
| 43 | + endpoint: int = None, timedRequestTimeoutMs: int = 3000, |
| 44 | + expected_status: Status = Status.Success): |
| 45 | + try: |
| 46 | + await self.send_single_cmd(cmd=Clusters.DeviceEnergyManagement.Commands.PowerAdjustRequest( |
| 47 | + power=power, |
| 48 | + duration=duration, |
| 49 | + cause=cause), |
| 50 | + endpoint=endpoint, |
| 51 | + timedRequestTimeoutMs=timedRequestTimeoutMs) |
| 52 | + |
| 53 | + asserts.assert_equal(expected_status, Status.Success) |
| 54 | + |
| 55 | + except InteractionModelError as e: |
| 56 | + asserts.assert_equal(e.status, expected_status, "Unexpected error returned") |
| 57 | + |
| 58 | + async def send_cancel_power_adjustment_command(self, endpoint: int = None, timedRequestTimeoutMs: int = 3000, |
| 59 | + expected_status: Status = Status.Success): |
| 60 | + try: |
| 61 | + await self.send_single_cmd(cmd=Clusters.DeviceEnergyManagement.Commands.CancelPowerAdjustRequest(), |
| 62 | + endpoint=endpoint, |
| 63 | + timedRequestTimeoutMs=timedRequestTimeoutMs) |
| 64 | + |
| 65 | + asserts.assert_equal(expected_status, Status.Success) |
| 66 | + |
| 67 | + except InteractionModelError as e: |
| 68 | + asserts.assert_equal(e.status, expected_status, "Unexpected error returned") |
| 69 | + |
| 70 | + async def send_start_time_adjust_request_command(self, requestedStartTime: int, |
| 71 | + cause: Clusters.Objects.DeviceEnergyManagement.Enums.CauseEnum, |
| 72 | + endpoint: int = None, timedRequestTimeoutMs: int = 3000, |
| 73 | + expected_status: Status = Status.Success): |
| 74 | + try: |
| 75 | + await self.send_single_cmd(cmd=Clusters.DeviceEnergyManagement.Commands.StartTimeAdjustRequest( |
| 76 | + requestedStartTime=requestedStartTime, |
| 77 | + cause=cause), |
| 78 | + endpoint=endpoint, |
| 79 | + timedRequestTimeoutMs=timedRequestTimeoutMs) |
| 80 | + |
| 81 | + asserts.assert_equal(expected_status, Status.Success) |
| 82 | + |
| 83 | + except InteractionModelError as e: |
| 84 | + asserts.assert_equal(e.status, expected_status, "Unexpected error returned") |
| 85 | + |
| 86 | + async def send_start_time_adjust_clear_command(self, |
| 87 | + endpoint: int = None, timedRequestTimeoutMs: int = 3000, |
| 88 | + expected_status: Status = Status.Success): |
| 89 | + try: |
| 90 | + await self.send_single_cmd(cmd=Clusters.DeviceEnergyManagement.Commands.StartTimeAdjustClear(), # StartTimeAdjustmentClear(), |
| 91 | + endpoint=endpoint, |
| 92 | + timedRequestTimeoutMs=timedRequestTimeoutMs) |
| 93 | + |
| 94 | + asserts.assert_equal(expected_status, Status.Success) |
| 95 | + |
| 96 | + except InteractionModelError as e: |
| 97 | + asserts.assert_equal(e.status, expected_status, "Unexpected error returned") |
| 98 | + |
| 99 | + async def send_cancel_request_command(self, |
| 100 | + endpoint: int = None, timedRequestTimeoutMs: int = 3000, |
| 101 | + expected_status: Status = Status.Success): |
| 102 | + try: |
| 103 | + await self.send_single_cmd(cmd=Clusters.DeviceEnergyManagement.Commands.CancelRequest(), |
| 104 | + endpoint=endpoint, |
| 105 | + timedRequestTimeoutMs=timedRequestTimeoutMs) |
| 106 | + |
| 107 | + asserts.assert_equal(expected_status, Status.Success) |
| 108 | + |
| 109 | + except InteractionModelError as e: |
| 110 | + asserts.assert_equal(e.status, expected_status, "Unexpected error returned") |
| 111 | + |
| 112 | + async def send_pause_request_command(self, duration: int, cause: |
| 113 | + Clusters.Objects.DeviceEnergyManagement.Enums.AdjustmentCauseEnum, |
| 114 | + endpoint: int = None, timedRequestTimeoutMs: int = 3000, |
| 115 | + expected_status: Status = Status.Success): |
| 116 | + try: |
| 117 | + await self.send_single_cmd(cmd=Clusters.DeviceEnergyManagement.Commands.PauseRequest( |
| 118 | + duration=duration, |
| 119 | + cause=cause), |
| 120 | + endpoint=endpoint, |
| 121 | + timedRequestTimeoutMs=timedRequestTimeoutMs) |
| 122 | + |
| 123 | + asserts.assert_equal(expected_status, Status.Success) |
| 124 | + |
| 125 | + except InteractionModelError as e: |
| 126 | + asserts.assert_equal(e.status, expected_status, "Unexpected error returned") |
| 127 | + |
| 128 | + async def send_resume_request_command(self, endpoint: int = None, timedRequestTimeoutMs: int = 3000, |
| 129 | + expected_status: Status = Status.Success): |
| 130 | + try: |
| 131 | + await self.send_single_cmd(cmd=Clusters.DeviceEnergyManagement.Commands.ResumeRequest(), |
| 132 | + endpoint=endpoint, |
| 133 | + timedRequestTimeoutMs=timedRequestTimeoutMs) |
| 134 | + |
| 135 | + asserts.assert_equal(expected_status, Status.Success) |
| 136 | + |
| 137 | + except InteractionModelError as e: |
| 138 | + asserts.assert_equal(e.status, expected_status, "Unexpected error returned") |
| 139 | + |
| 140 | + async def send_modify_forecast_request_command(self, forecastID: int, |
| 141 | + slotAdjustments: list[Clusters.DeviceEnergyManagement.Structs.SlotAdjustmentStruct], |
| 142 | + cause: Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum, |
| 143 | + endpoint: int = None, timedRequestTimeoutMs: int = 3000, |
| 144 | + expected_status: Status = Status.Success): |
| 145 | + try: |
| 146 | + await self.send_single_cmd(cmd=Clusters.DeviceEnergyManagement.Commands.ModifyForecastRequest(forecastID=forecastID, |
| 147 | + slotAdjustments=slotAdjustments, |
| 148 | + cause=cause), |
| 149 | + endpoint=endpoint, |
| 150 | + timedRequestTimeoutMs=timedRequestTimeoutMs) |
| 151 | + |
| 152 | + asserts.assert_equal(expected_status, Status.Success) |
| 153 | + |
| 154 | + except InteractionModelError as e: |
| 155 | + asserts.assert_equal(e.status, expected_status, "Unexpected error returned") |
| 156 | + |
| 157 | + async def send_request_constraint_based_forecast(self, constraintList: list[Clusters.DeviceEnergyManagement.Structs.ConstraintsStruct], |
| 158 | + cause: Clusters.DeviceEnergyManagement.Enums.AdjustmentCauseEnum, |
| 159 | + endpoint: int = None, timedRequestTimeoutMs: int = 3000, |
| 160 | + expected_status: Status = Status.Success): |
| 161 | + try: |
| 162 | + await self.send_single_cmd(cmd=Clusters.DeviceEnergyManagement.Commands.RequestConstraintBasedForecast(constraints=constraintList, |
| 163 | + cause=cause), |
| 164 | + endpoint=endpoint, |
| 165 | + timedRequestTimeoutMs=timedRequestTimeoutMs) |
| 166 | + |
| 167 | + asserts.assert_equal(expected_status, Status.Success) |
| 168 | + |
| 169 | + except InteractionModelError as e: |
| 170 | + asserts.assert_equal(e.status, expected_status, "Unexpected error returned") |
| 171 | + |
| 172 | + def print_forecast(self, forecast): |
| 173 | + for index, slot in enumerate(forecast.slots): |
| 174 | + logging.info( |
| 175 | + f" [{index}] MinDuration: {slot.minDuration} MaxDuration: {slot.maxDuration} DefaultDuration: {slot.defaultDuration}") |
| 176 | + logging.info(f" ElapseSlotTime: {slot.elapsedSlotTime} RemainingSlotTime: {slot.remainingSlotTime}") |
| 177 | + logging.info( |
| 178 | + f" SlotIsPausable: {slot.slotIsPausable} MinPauseDuration: {slot.minPauseDuration} MaxPauseDuration: {slot.maxPauseDuration}") |
| 179 | + logging.info(f" ManufacturerESAState: {slot.manufacturerESAState}") |
| 180 | + logging.info(f" NominalPower: {slot.nominalPower} MinPower: {slot.minPower} MaxPower: {slot.maxPower}") |
| 181 | + logging.info(f" MinPowerAdjustment: {slot.minPowerAdjustment} MaxPowerAdjustment: {slot.maxPowerAdjustment}") |
| 182 | + logging.info( |
| 183 | + f" MinDurationAdjustment: {slot.minDurationAdjustment} MaxDurationAdjustment: {slot.maxDurationAdjustment}") |
| 184 | + if slot.costs is not None: |
| 185 | + for cost_index, cost in enumerate(slot): |
| 186 | + logging.info( |
| 187 | + f" Cost: [{cost_index}] CostType:{cost.costType} Value: {cost.value} DecimalPoints: {cost.decimalPoints} Currency: {cost.currency}") |
| 188 | + |
| 189 | + def get_current_utc_time_in_seconds(self): |
| 190 | + microseconds_in_second = 1000000 |
| 191 | + return int(utc_time_in_matter_epoch()/microseconds_in_second) |
| 192 | + |
| 193 | + async def send_test_event_trigger_power_adjustment(self): |
| 194 | + await self.send_test_event_triggers(eventTrigger=0x0098000000000000) |
| 195 | + |
| 196 | + async def send_test_event_trigger_power_adjustment_clear(self): |
| 197 | + await self.send_test_event_triggers(eventTrigger=0x0098000000000001) |
| 198 | + |
| 199 | + async def send_test_event_trigger_user_opt_out_local(self): |
| 200 | + await self.send_test_event_triggers(eventTrigger=0x0098000000000002) |
| 201 | + |
| 202 | + async def send_test_event_trigger_user_opt_out_grid(self): |
| 203 | + await self.send_test_event_triggers(eventTrigger=0x0098000000000003) |
| 204 | + |
| 205 | + async def send_test_event_trigger_user_opt_out_clear_all(self): |
| 206 | + await self.send_test_event_triggers(eventTrigger=0x0098000000000004) |
| 207 | + |
| 208 | + async def send_test_event_trigger_start_time_adjustment(self): |
| 209 | + await self.send_test_event_triggers(eventTrigger=0x0098000000000005) |
| 210 | + |
| 211 | + async def send_test_event_trigger_start_time_adjustment_clear(self): |
| 212 | + await self.send_test_event_triggers(eventTrigger=0x0098000000000006) |
| 213 | + |
| 214 | + async def send_test_event_trigger_pausable(self): |
| 215 | + await self.send_test_event_triggers(eventTrigger=0x0098000000000007) |
| 216 | + |
| 217 | + async def send_test_event_trigger_pausable_next_slot(self): |
| 218 | + await self.send_test_event_triggers(eventTrigger=0x0098000000000008) |
| 219 | + |
| 220 | + async def send_test_event_trigger_pausable_clear(self): |
| 221 | + await self.send_test_event_triggers(eventTrigger=0x0098000000000009) |
| 222 | + |
| 223 | + async def send_test_event_trigger_forecast_adjustment(self): |
| 224 | + await self.send_test_event_triggers(eventTrigger=0x009800000000000A) |
| 225 | + |
| 226 | + async def send_test_event_trigger_forecast_adjustment_next_slot(self): |
| 227 | + await self.send_test_event_triggers(eventTrigger=0x009800000000000B) |
| 228 | + |
| 229 | + async def send_test_event_trigger_forecast_adjustment_clear(self): |
| 230 | + await self.send_test_event_triggers(eventTrigger=0x009800000000000C) |
| 231 | + |
| 232 | + async def send_test_event_trigger_constraint_based_adjustment(self): |
| 233 | + await self.send_test_event_triggers(eventTrigger=0x009800000000000D) |
| 234 | + |
| 235 | + async def send_test_event_trigger_constraint_based_adjustment_clear(self): |
| 236 | + await self.send_test_event_triggers(eventTrigger=0x009800000000000E) |
| 237 | + |
| 238 | + async def send_test_event_trigger_forecast(self): |
| 239 | + await self.send_test_event_triggers(eventTrigger=0x009800000000000F) |
| 240 | + |
| 241 | + async def send_test_event_trigger_forecast_clear(self): |
| 242 | + await self.send_test_event_triggers(eventTrigger=0x0098000000000010) |
0 commit comments