diff --git a/docs/guides/darwin.md b/docs/guides/darwin.md index 2a84067da92ed0..d44e44cc04afd9 100644 --- a/docs/guides/darwin.md +++ b/docs/guides/darwin.md @@ -1,8 +1,21 @@ -# Testing with Apple Devices +# Matter Development and Testing with/on Apple Devices -### Matter is the foundation for connected things. +### Overview + +- To develop a Matter Application, learn more about how to develop with Matter + on Apple's platforms [here](https://developer.apple.com/apple-home/matter/) + + - Additional documentation about the Matter Support API is + [here](https://developer.apple.com/documentation/mattersupport) -Learn more about Matter [here](https://buildwithmatter.com/) +### Filing Issues + +- An issues related to Apple Home, or Development on Apple's Platforms should + be reported using Feedback Assistant as described + [below](#providing-feedback-to-apple) + +- Any issues related to the Matter SDK should be reported to the project + [here](https://github.com/project-chip/connectedhomeip/issues) ## Source Compatibility @@ -12,20 +25,18 @@ changes are present in the release for testing. Listed are the Current SHAs: -- iOS/iPadOS/tvOS 16.1: - [`33f6a910cd9a8a0cfdd7088e2f43efd2f7f566a7`](https://github.com/project-chip/connectedhomeip/commits/33f6a910cd9a8a0cfdd7088e2f43efd2f7f566a7) - -- iOS/iPadOS/tvOS 16.2 and 16.3: - [`c279578c5bc37f117335aa96cec6c5552f070cc0`](https://github.com/project-chip/connectedhomeip/commits/c279578c5bc37f117335aa96cec6c5552f070cc0) +- Latest macOS/tvOS/iOS/iPadOS: + [`2ee90eba27676950fa2f4ef96597d9696f510d5d`](https://github.com/project-chip/connectedhomeip/commits/2ee90eba27676950fa2f4ef96597d9696f510d5d) +- Larger list of SHAs are [here](#release-to-sha-mappings) ## Supported Platforms for Matter Device Testing - Matter is supported by iOS/tvOS/iPadOS/watchOS/HomePod/AppleTV starting with 16.1 - - To test your Matter device with Apple's platforms, all you need is a device with a supported OS -- Pairing via QR Code or Setup Code is supported directly from the Home App +- Pairing via QR Code or Setup Code is supported directly from the Home App, + or any third party Application - Please proceed to [this section](#testing-your-matter-device-with-apple-home) if you're developing a new device @@ -46,14 +57,6 @@ For Context: This is a mapping of platform to OS - HomePod _(tvOS)_ - Apple Watch _(watchOS)_ -### Overview - -- To develop a Matter Application, learn more about how to develop with Matter - on Apple's platforms [here](https://developer.apple.com/apple-home/matter/) - - - Additional documentation about the Matter Support API is - [here](https://developer.apple.com/documentation/mattersupport) - ### Setup Requirements for Application Development - Devices must support BLE pairing, and have it enabled @@ -122,9 +125,7 @@ For Context: This is a mapping of platform to OS ### Enable Developer Mode on your Apple Device -##### Note: Developer mode is a great way to get logs from your device as well as - -enables other useful developer tools +##### Note: Developer mode is a great way to get logs from your device as well as enable other useful developer tools To enable developer mode, please follow the instructions [here](https://developer.apple.com/documentation/xcode/enabling-developer-mode-on-a-device) @@ -137,30 +138,96 @@ chip-tool will require installing the on MacOS or iOS/iPadOS. - Download the Bluetooth Central Matter Client Developer Mode profile and - install it on a iOS/iPadOS 16.1 beta 3 and MacOS 13.1 beta 3 or later - system. + install it on a supported system. - For _macOS_, Profile can be installed via Settings->Privacy & Security->Profiles - For _iOS/iPadOS_, If necessary, email the profile or use AirDrop to - transfer the profile to the _iOS/iPadOS 16 beta 3_ device. + transfer the profile to the _iOS/iPadOS_ device. - Restart your system - For _iOS/iPadOS_, enable Developer Mode. Refer to [this developer page](https://developer.apple.com/documentation/xcode/enabling-developer-mode-on-a-device) - Compile chip-tool for macOS or CHIP Tool for iOS +## Supported Device Types in Apple Home + +##### Note: Not exhaustive, and may be out of date, see [below](#apple-home-development-guide) for more information + +| Type | Decimal | HEX | Notes | +| ----------------------- | ------- | ---- | ------------------------------------------------ | +| On/Off Light | 256 | 0100 | | +| Dimming Light | 257 | 0101 | | +| On/Off Plug-In Unit | 266 | 010A | | +| Dimmable Plug-In Unit | 267 | 010B | | +| On/Off Light Switch | 259 | 0103 | Requires both On/Off Client and Server | +| Dimmer Switch | 260 | 0104 | Requires both On/Off Client and Server | +| Generic Switch (button) | 15 | 000F | Supports momentary switch only, and not latching | +| Contact Sensor | 21 | 0015 | | +| Light Sensor | 262 | 0106 | | +| Occupancy Sensor | 263 | 0107 | | +| Temperature Sensor | 770 | 0302 | | +| Humidity Sensor | 775 | 0307 | | +| Air Quality Sensor | 44 | 002C | | +| Door Lock | 10 | 000A | | +| Window Covering | 514 | 0202 | | +| Heating/Cooling Unit | 768 | 0300 | | +| Thermostat | 769 | 0301 | | +| Fan | 43 | 002B | | +| Air Purifier | 45 | 002D | | +| Temperature Color Light | 268 | 010C | | +| Enhanced Color Light | 269 | 010D | | +| Bridges | 14 | 000E | | +| Robot Vacuum Cleaner | 116 | 0074 | Announced, not yet supported | + +## Additional Device Type Support on Apple's Platforms + +Apple's platforms support all device types available in the Matter SDK, so +developers can use Matter.framework to develop their own applications that can +add devices to Apple Home, or create their own Fabric to manage devices as well. + +Please see documentation about `-[HMAccessory matterNodeID]` +[here](https://developer.apple.com/documentation/homekit/hmaccessory/matternodeid-5zfqo) +which allows you to use Matter.framework to interact with Matter Devices +directly that are paired into Apple Home. + +## Apple Home Development Guide + +Please see [here](https://developer.apple.com/apple-home/) for more general +information about developing an Application or a Device for Apple Home. This +includes information about +[best practices](https://developer.apple.com/apple-home/downloads/Matter-Accessory-Best-Practices-for-Apple-Home.pdf), +[platform developer API](https://developer.apple.com/apple-home/matter/), +[OTA Updates](https://developer.apple.com/accessories/Apple-Matter-OTA-User-Guide.pdf), +general adoption Q&A, and the "Works with Apple Home" badge. + +## General Matter Platform Development Guide + +##### Getting the SDK Ready + +##### Note: Most platforms have very similar, if not the same configuration requirements + +1. Checkout and setup + [Matter repo](https://github.com/project-chip/connectedhomeip.git) as per the + instructions above. +2. Find and edit one of the platform + [examples](https://github.com/project-chip/connectedhomeip/tree/master/examples) + to support the fixed device types above. +3. Read the [platform guides](README.md) on how set up the hardware + - There is a list of more detailed guides [here](#platform-guides) + ## Testing your Matter Device with Apple Home -1. Clone the [Matter repo](https://github.com/project-chip/connectedhomeip.git) +1. Clone the + [Matter repository](https://github.com/project-chip/connectedhomeip.git) 2. Checkout the specific commit hash (from [above](#source-compatibility)) for maximum compatibility with your installed release: - - Example command for SHA `c279578c5bc37f117335aa96cec6c5552f070cc0`: - `$ git checkout c279578c5bc37f117335aa96cec6c5552f070cc0` + - Example command for SHA `2ee90eba27676950fa2f4ef96597d9696f510d5d`: + `$ git checkout 2ee90eba27676950fa2f4ef96597d9696f510d5d` -In order to work with iOS/iPadOS/tvOS 16.1 or greater, device types as defined -in the Matter Device Library spec are used to determine accessory categories. -Ensure the right device type is set for each endpoint. +In order to work with iOS/iPadOS/tvOS, device types as defined in the Matter +Device Library spec are used to determine accessory categories. Ensure the right +device type is set for each endpoint. - For the `all-clusters-app` as an example, this can be set in `FIXED_DEVICE_TYPES`, `FIXED_DEVICE_TYPE_OFFSETS`, and @@ -190,29 +257,11 @@ Example: } ``` -- Supported device types are (not exhaustive): - -| Type | Decimal | HEX | -| ------------------ | ------- | ---- | -| Lightbulb | 256 | 0100 | -| Lightbulb + Dimmer | 257 | 0101 | -| Switch | 259 | 0103 | -| Contact Sensor | 21 | 0015 | -| Door Lock | 10 | 000A | -| Light Sensor | 262 | 0106 | -| Occupancy Sensor | 263 | 0107 | -| Outlet | 266 | 010A | -| Color Bulb | 268 | 010C | -| Window Covering | 514 | 0202 | -| Thermostat | 769 | 0301 | -| Temperature Sensor | 770 | 0302 | -| Flow Sensor | 774 | 0306 | - -#### Examples of how to setup devices +### Examples of how to setup devices ##### Case study 1: Configuring a development M5Stack, as a multi-device to work with iOS/iPadOS/tvOS -##### Note: These instructions are specific to getting started with the (Matter-provided) `all-clusters-app` on an ESP32-based M5Stack, however can be generalised to work on most platforms ([more listed below](#guides)) +##### Note: These instructions are specific to getting started with the (Matter-provided) `all-clusters-app` on an ESP32-based M5Stack, however can be generalised to work on most platforms ([more listed below](#platform-guides)) 1. Checkout and setup [Matter repo](https://github.com/project-chip/connectedhomeip.git) as per the @@ -235,21 +284,7 @@ Example: initialize your development environment, compile the firmware and flash your hardware. -#### General Matter Platform Development Guide - -##### Getting the SDK Ready - -##### Note: Most platforms have very similar, if not the same configuration requirements - -1. Checkout and setup - [Matter repo](https://github.com/project-chip/connectedhomeip.git) as per the - instructions above. -2. Find and edit one of the platform - [examples](https://github.com/project-chip/connectedhomeip/tree/master/examples) - to support the fixed device types above. -3. Read the [platform guides](README.md) on how set up the hardware - -##### Guides +### Platform Guides - [Bouffalo Lab](/examples/lighting-app/bouffalolab/README.md) - [EFR32 Window Covering](/examples/window-app/silabs/README.md) @@ -300,26 +335,20 @@ Example: - [Apple Home Profile](https://developer.apple.com/bug-reporting/profiles-and-logs/?platform=tvos&name=homekit) - [Network Profile](https://developer.apple.com/bug-reporting/profiles-and-logs/?platform=tvos&name=network) - [mDNS Profile](https://developer.apple.com/bug-reporting/profiles-and-logs/?platform=tvos&name=mdns) - -### Release Notes & Known Issues - -- Please refer to the iOS/iPadOS 16.1 - [Release Notes](https://developer.apple.com/documentation/ios-ipados-release-notes/ios-16_1-release-notes) - for currently known issues. -- Please refer to the iOS/iPadOS 16.2 - [Release Notes](https://developer.apple.com/documentation/ios-ipados-release-notes/ios-ipados-16_2-release-notes) - for currently known issues -- Please refer to the iOS & iPadOS 16.3 - [Release Notes](https://developer.apple.com/documentation/ios-ipados-release-notes/ios-ipados-16_3-release-notes) - for currently known issues -- Please refer to the iOS & iPadOS 16.4 - [Release Notes](https://developer.apple.com/documentation/ios-ipados-release-notes/ios-ipados-16_4-release-notes) - for currently known issues -- Please refer to the iOS & iPadOS 16.5 - [Release Notes](https://developer.apple.com/documentation/ios-ipados-release-notes/ios-ipados-16_5-release-notes) - for currently known issues -- An issues related to Apple Home integration should be reported - [feedback](#providing-feedback-to-apple) as described in this section - -- Any issues related to the Matter SDK should be reported to the project - [here](https://github.com/project-chip/connectedhomeip/issues) + - Any Thread device: + [Thread Profile](https://developer.apple.com/bug-reporting/profiles-and-logs/?name=HomeThread) + +### Release to SHA Mappings + +| Platform | Release Version | SHA | +| -------- | --------------- | ------------------------------------------ | +| iOS | 17.6.1 | `2ee90eba27676950fa2f4ef96597d9696f510d5d` | +| iPadOS | 17.6.1 | `2ee90eba27676950fa2f4ef96597d9696f510d5d` | +| tvOS | 17.6 | `2ee90eba27676950fa2f4ef96597d9696f510d5d` | +| macOS | 14.6.1 | `2ee90eba27676950fa2f4ef96597d9696f510d5d` | +| iOS | 17.5.1 | `d09b5ac98f4d7d8b9f2c307f55ab5462576623a5` | +| iPadOS | 17.5.1 | `d09b5ac98f4d7d8b9f2c307f55ab5462576623a5` | +| tvOS | 17.5.1 | `d09b5ac98f4d7d8b9f2c307f55ab5462576623a5` | +| macOS | 14.5 | `d09b5ac98f4d7d8b9f2c307f55ab5462576623a5` | +| iOS | 16.7.8 | `83f7a2fe136e0b746db09f1d19e36c693a634b66` | +| iPadOS | 16.7.8 | `83f7a2fe136e0b746db09f1d19e36c693a634b66` | 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 c31a0c8ca9292d..46debb8a6cd363 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 @@ -4386,7 +4386,7 @@ cluster ElectricalEnergyMeasurement = 145 { /** This cluster is used to allow clients to control the operation of a hot water heating appliance so that it can be used with energy management. */ provisional cluster WaterHeaterManagement = 148 { - revision 1; + revision 2; enum BoostStateEnum : enum8 { kInactive = 0; @@ -4398,7 +4398,7 @@ provisional cluster WaterHeaterManagement = 148 { kTankPercent = 0x2; } - bitmap WaterHeaterDemandBitmap : bitmap8 { + bitmap WaterHeaterHeatSourceBitmap : bitmap8 { kImmersionElement1 = 0x1; kImmersionElement2 = 0x2; kHeatPump = 0x4; @@ -4406,16 +4406,24 @@ provisional cluster WaterHeaterManagement = 148 { kOther = 0x10; } - bitmap WaterHeaterTypeBitmap : bitmap8 { - kImmersionElement1 = 0x1; - kImmersionElement2 = 0x2; - kHeatPump = 0x4; - kBoiler = 0x8; - kOther = 0x10; + struct WaterHeaterBoostInfoStruct { + elapsed_s duration = 0; + optional boolean oneShot = 1; + optional boolean emergencyBoost = 2; + optional temperature temporarySetpoint = 3; + optional percent targetPercentage = 4; + optional percent targetReheat = 5; + } + + info event BoostStarted = 0 { + WaterHeaterBoostInfoStruct boostInfo = 0; } - readonly attribute WaterHeaterTypeBitmap heaterTypes = 0; - readonly attribute WaterHeaterDemandBitmap heatDemand = 1; + info event BoostEnded = 1 { + } + + readonly attribute WaterHeaterHeatSourceBitmap heaterTypes = 0; + readonly attribute WaterHeaterHeatSourceBitmap heatDemand = 1; readonly attribute optional int16u tankVolume = 2; readonly attribute optional energy_mwh estimatedHeatRequired = 3; readonly attribute optional percent tankPercentage = 4; @@ -4428,12 +4436,7 @@ provisional cluster WaterHeaterManagement = 148 { readonly attribute int16u clusterRevision = 65533; request struct BoostRequest { - elapsed_s duration = 0; - optional boolean oneShot = 1; - optional boolean emergencyBoost = 2; - optional temperature temporarySetpoint = 3; - optional percent targetPercentage = 4; - optional percent targetReheat = 5; + WaterHeaterBoostInfoStruct boostInfo = 0; } /** Allows a client to request that the water heater is put into a Boost state. */ diff --git a/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h b/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h index ad73239e77990d..27d8cd7cd96ecf 100644 --- a/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h +++ b/examples/all-clusters-app/all-clusters-common/include/WhmDelegate.h @@ -56,30 +56,52 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate *********************************************************************************/ /** - * @brief Delegate should implement a handler to start boosting the water temperature as required. - * Upon receipt, the Water Heater SHALL transition into the BOOST state, which SHALL cause the - * water in the tank (or the TargetPercentage of the water, if included) to be heated towards - * the set point (or the TemporarySetpoint, if included), which in turn may cause a call for heat, - * even if the mode is OFF, or is TIMED and it is during one of the Off periods. + * @brief Delegate should implement a handler to start boosting the water + * temperature as required. Upon receipt, the Water Heater SHALL + * transition into the BOOST state, which SHALL cause the water in + * the tank (or the TargetPercentage of the water, if included) to be + * heated towards the set point (or the TemporarySetpoint, if + * included), which in turn may cause a call for heat, even if the + * mode is OFF, or is TIMED and it is during one of the Off periods. * - * @param duration Indicates the time period in seconds for which the BOOST state is activated before it - * automatically reverts to the previous mode (e.g. OFF, MANUAL or TIMED). - * @param oneShot Indicates whether the BOOST state should be automatically canceled once the hot water has - * first reached the set point temperature (or the TemporarySetpoint temperature, if specified) - * for the TargetPercentage (if specified). - * @param emergencyBoost Indicates that the consumer wants the water to be heated as quickly as practicable. - * This MAY cause multiple heat sources to be activated (e.g. a heat pump and direct - * electric heating element). - * @param temporarySetpoint Indicates the target temperature to which to heat the hot water for this Boost command. - * It SHALL be used instead of the normal set point temperature whilst the BOOST state is active. - * @param targetPercentage If the tank supports the TankPercent feature, this field indicates the amount of water - * that SHALL be heated by this Boost command before the heater is switched off. - * @param targetReheat If the tank supports the TankPercent feature, and the heating by this Boost command has ceased - * because the TargetPercentage of the water in the tank has been heated to the set point (or - * TemporarySetpoint if included), this field indicates the percentage to which the hot water in - * the tank SHALL be allowed to fall before again beginning to reheat it. + * @param duration Indicates the time period in seconds for which + * the BOOST state is activated before it + * automatically reverts to the previous mode + * (e.g. OFF, MANUAL or TIMED). * - * @return Success if the boost command is accepted; otherwise the command SHALL be rejected with appropriate error. + * @param oneShot Indicates whether the BOOST state should be + * automatically canceled once the hot water has + * first reached the set point temperature (or the + * TemporarySetpoint temperature, if specified) + * for the TargetPercentage (if specified). + * + * @param emergencyBoost Indicates that the consumer wants the water to + * be heated as quickly as practicable. This MAY + * cause multiple heat sources to be activated + * (e.g. a heat pump and direct electric heating + * element). + * + * @param temporarySetpoint Indicates the target temperature to which to + * heat the hot water for this Boost command. It + * SHALL be used instead of the normal set point + * temperature whilst the BOOST state is active. + * + * @param targetPercentage If the tank supports the TankPercent feature, + * this field indicates the amount of water that + * SHALL be heated by this Boost command before + * the heater is switched off. + * + * @param targetReheat If the tank supports the TankPercent feature, + * and the heating by this Boost command has + * ceased because the TargetPercentage of the + * water in the tank has been heated to the set + * point (or TemporarySetpoint if included), this + * field indicates the percentage to which the hot + * water in the tank SHALL be allowed to fall + * before again beginning to reheat it. + * + * @return Success if the boost command is accepted; otherwise the command + * SHALL be rejected with appropriate error. */ Protocols::InteractionModel::Status HandleBoost(uint32_t duration, Optional oneShot, Optional emergencyBoost, Optional temporarySetpoint, Optional targetPercentage, @@ -87,8 +109,8 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate /** * @brief Delegate should implement a handler to cancel a boost command. - * Upon receipt, the Water Heater SHALL transition back from the BOOST state to the previous mode - * (e.g. OFF, MANUAL or TIMED). + * Upon receipt, the Water Heater SHALL transition back from the + * BOOST state to the previous mode (e.g. OFF, MANUAL or TIMED). * * @return It should report SUCCESS if successful and FAILURE otherwise. */ @@ -96,8 +118,8 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate // ------------------------------------------------------------------ // Get attribute methods - BitMask GetHeaterTypes() override; - BitMask GetHeatDemand() override; + BitMask GetHeaterTypes() override; + BitMask GetHeatDemand() override; uint16_t GetTankVolume() override; int64_t GetEstimatedHeatRequired() override; Percent GetTankPercentage() override; @@ -105,18 +127,18 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate // ------------------------------------------------------------------ // Set attribute methods - void SetHeaterTypes(BitMask heaterTypes); - void SetHeatDemand(BitMask heatDemand); + void SetHeaterTypes(BitMask heaterTypes); + void SetHeatDemand(BitMask heatDemand); void SetTankVolume(uint16_t tankVolume); void SetEstimatedHeatRequired(int64_t estimatedHeatRequired); void SetTankPercentage(Percent tankPercentage); void SetBoostState(BoostStateEnum boostState); - /********************************************************************************* + /*************************************************************************** * * WaterHeaterManagementDelegate specific methods * - *********************************************************************************/ + ***************************************************************************/ /** * @brief Set the Water Header Mode and act accordingly. @@ -155,36 +177,44 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate void HandleBoostTimerExpiry(); /** - * Determines whether the tank water temperature has reached the target temperature. + * Determines whether the tank water temperature has reached the target + * temperature. * - * @return Returns True is tank water temperature has reached the target temperature, False otherwise. + * @return Returns True is tank water temperature has reached the target + * temperature, False otherwise. */ bool HasWaterTemperatureReachedTarget() const; /** * Simulates water being drawn from the water tank. * - * @param percentageReplaced The % of water being replaced with water with a temperature of replacedWaterTemperature. - * @param replacedWaterTemperature The temperature of the percentageReplaced water. + * @param percentageReplaced The % of water being replaced with water with + * a temperature of replacedWaterTemperature. + * + * @param replacedWaterTemperature The temperature of the + * percentageReplaced water. */ void DrawOffHotWater(chip::Percent percentageReplaced, uint16_t replacedWaterTemperature); private: /** - * @brief Determine whether heating needs to be turned on or off or left as is. + * @brief Determine whether heating needs to be turned on or off or left as + * is. * - * @param heatingOp[out] Set as determined whether heating needs to be turned on/off or left unchanged. + * @param heatingOp[out] Set as determined whether heating needs to be + * turned on/off or left unchanged. * - * @return Success if the heating operation could be determined otherwise returns with appropriate error. + * @return Success if the heating operation could be determined otherwise + * returns with appropriate error. */ Protocols::InteractionModel::Status DetermineIfChangingHeatingState(HeatingOp & heatingOp); private: - /********************************************************************************* + /*************************************************************************** * * WaterHeaterManagementDelegate specific attributes * - *********************************************************************************/ + ***************************************************************************/ // Need the following so can determine which features are supported WaterHeaterManagement::Instance * mpWhmInstance; @@ -203,34 +233,44 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate // Boost command parameters - // This field SHALL indicate whether the BOOST state should be automatically canceled once the hot water has first reached the - // set point temperature (or the TemporarySetpoint temperature, if specified) for the TargetPercentage (if specified). + // This field SHALL indicate whether the BOOST state should be automatically + // canceled once the hot water has first reached the set point temperature + // (or the TemporarySetpoint temperature, if specified) for the + // TargetPercentage (if specified). Optional mBoostOneShot; - // This field indicates that the consumer wants the water to be heated as quickly as practicable. This MAY cause multiple heat - // sources to be activated (e.g. a heat pump and direct electric heating element). + // This field indicates that the consumer wants the water to be heated as + // quickly as practicable. This MAY cause multiple heat sources to be + // activated (e.g. a heat pump and direct electric heating element). Optional mBoostEmergencyBoost; - // This field indicates the target temperature to which to heat the hot water for this Boost command. It SHALL be used instead - // of the normal set point temperature whilst the BOOST state is active. + // This field indicates the target temperature to which to heat the hot + // water for this Boost command. It SHALL be used instead of the normal set + // point temperature whilst the BOOST state is active. Optional mBoostTemporarySetpoint; - // If the tank supports the TankPercent feature, this field indicates the amount of water that SHALL be heated by this Boost - // command before the heater is switched off. This field is optional, however it SHALL be included if the TargetReheat field is - // included. + // If the tank supports the TankPercent feature, this field indicates the + // amount of water that SHALL be heated by this Boost command before the + // heater is switched off. This field is optional, however it SHALL be + // included if the TargetReheat field is included. Optional mBoostTargetPercentage; - // If the tank supports the TankPercent feature, and the heating by this Boost command has ceased because the TargetPercentage - // of the water in the tank has been heated to the set point (or TemporarySetpoint if included), this field indicates the - // percentage to which the hot water in the tank SHALL be allowed to fall before again beginning to reheat it. For example if - // the TargetPercentage was 80%, and the TargetReheat was 40%, then after initial heating to 80% hot water, the tank may have - // hot water drawn off until only 40% hot water remains. At this point the heater will begin to heat back up to 80% of hot - // water. If this field and the OneShot field were both omitted, heating would begin again after any water draw which reduced - // the TankPercentage below 80%. + // If the tank supports the TankPercent feature, and the heating by this + // Boost command has ceased because the TargetPercentage of the water in the + // tank has been heated to the set point (or TemporarySetpoint if included), + // this field indicates the percentage to which the hot water in the tank + // SHALL be allowed to fall before again beginning to reheat it. For example + // if the TargetPercentage was 80%, and the TargetReheat was 40%, then after + // initial heating to 80% hot water, the tank may have hot water drawn off + // until only 40% hot water remains. At this point the heater will begin to + // heat back up to 80% of hot water. If this field and the OneShot field + // were both omitted, heating would begin again after any water draw which + // reduced the TankPercentage below 80%. Optional mBoostTargetReheat; - // Track whether the water temperature has reached the water temperature specified in the boost command. Used in conjunction - // with the boost command boostTargetReheat parameter + // Track whether the water temperature has reached the water temperature + // specified in the boost command. Used in conjunction with the boost + // command boostTargetReheat parameter bool mBoostTargetTemperatureReached; /********************************************************************************* @@ -239,28 +279,35 @@ class WaterHeaterManagementDelegate : public WaterHeaterManagement::Delegate * *********************************************************************************/ - // This attribute SHALL indicate the methods to call for heat that the controller supports. If a bit is set then the controller - // supports the corresponding method. - BitMask mHeaterTypes; + // This attribute SHALL indicate the methods to call for heat that the + // controller supports. If a bit is set then the controller supports the + // corresponding method. + BitMask mHeaterTypes; - // This attribute SHALL indicate if the controller is asking for heat. If a bit is set then the corresponding call for heat is - // active. - BitMask mHeatDemand; + // This attribute SHALL indicate if the controller is asking for heat. If a + // bit is set then the corresponding call for heat is active. + BitMask mHeatDemand; - // This attribute SHALL indicate the volume of water that the hot water tank can hold (in units of Litres). This allows an - // energy management system to estimate the required heating energy needed to reach the target temperature. + // This attribute SHALL indicate the volume of water that the hot water tank + // can hold (in units of Litres). This allows an energy management system to + // estimate the required heating energy needed to reach the target + // temperature. uint16_t mTankVolume; - // This attribute SHALL indicate the estimated heat energy needed to raise the water temperature to the target setpoint. This - // can be computed by taking the specific heat capacity of water (4182 J/kg °C) and by knowing the current temperature of the - // water, the tank volume and target temperature. + // This attribute SHALL indicate the estimated heat energy needed to raise + // the water temperature to the target setpoint. This can be computed by + // taking the specific heat capacity of water (4182 J/kg °C) and by knowing + // the current temperature of the water, the tank volume and target + // temperature. int64_t mEstimatedHeatRequired; - // This attribute SHALL indicate an approximate level of hot water stored in the tank, which may help consumers understand the - // amount of hot water remaining in the tank. + // This attribute SHALL indicate an approximate level of hot water stored in + // the tank, which may help consumers understand the amount of hot water + // remaining in the tank. Percent mTankPercentage; - // This attribute SHALL indicate if the BOOST state, as triggered by a Boost command, is currently active. + // This attribute SHALL indicate if the BOOST state, as triggered by a Boost + // command, is currently active. BoostStateEnum mBoostState; }; diff --git a/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h b/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h index d51e6a62f2021c..b56aa24abf491f 100644 --- a/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h +++ b/examples/all-clusters-app/all-clusters-common/include/WhmManufacturer.h @@ -62,7 +62,7 @@ class WhmManufacturer /** * @brief Called to determine which heating sources to use, */ - BitMask DetermineHeatingSources(); + BitMask DetermineHeatingSources(); /** * @brief Turn the heating of the water tank on. @@ -83,23 +83,44 @@ class WhmManufacturer /** * @brief Called to handle a boost command. * - * @param duration Indicates the time period in seconds for which the BOOST state is activated before it automatically reverts - * to the previous mode (e.g. OFF, MANUAL or TIMED). - * @param oneShot Indicates whether the BOOST state should be automatically canceled once the hot water has first reached the - * set point temperature (or the TemporarySetpoint temperature, if specified) for the TargetPercentage (if - * specified). - * @param emergencyBoost Indicates that the consumer wants the water to be heated as quickly as practicable. This MAY cause - * multiple heat sources to be activated (e.g. a heat pump and direct electric heating element). - * @param temporarySetpoint Indicates the target temperature to which to heat the hot water for this Boost command. It SHALL be - * used instead of the normal set point temperature whilst the BOOST state is active. - * @param targetPercentage If the tank supports the TankPercent feature, this field indicates the amount of water that SHALL be - * heated by this Boost command before the heater is switched off. - * @param targetReheat If the tank supports the TankPercent feature, and the heating by this Boost command has ceased because - * the TargetPercentage of the water in the tank has been heated to the set point (or TemporarySetpoint if - * included), this field indicates the percentage to which the hot water in the tank SHALL be allowed to fall before again - * beginning to reheat it. + * @param duration Indicates the time period in seconds for which + * the BOOST state is activated before it + * automatically reverts to the previous mode + * (e.g. OFF, MANUAL or TIMED). * - * @return Success if the boost command is successful; otherwise return the appropriate error. + * @param oneShot Indicates whether the BOOST state should be + * automatically canceled once the hot water has + * first reached the set point temperature (or the + * TemporarySetpoint temperature, if specified) + * for the TargetPercentage (if specified). + * + * @param emergencyBoost Indicates that the consumer wants the water to + * be heated as quickly as practicable. This MAY + * cause multiple heat sources to be activated + * (e.g. a heat pump and direct electric heating + * element). + * + * @param temporarySetpoint Indicates the target temperature to which to + * heat the hot water for this Boost command. It + * SHALL be used instead of the normal set point + * temperature whilst the BOOST state is active. + * + * @param targetPercentage If the tank supports the TankPercent feature, + * this field indicates the amount of water that + * SHALL be heated by this Boost command before + * the heater is switched off. + * + * @param targetReheat If the tank supports the TankPercent feature, + * and the heating by this Boost command has + * ceased because the TargetPercentage of the + * water in the tank has been heated to the set + * point (or TemporarySetpoint if included), this + * field indicates the percentage to which the hot + * water in the tank SHALL be allowed to fall + * before again beginning to reheat it. + * + * @return Success if the boost command is successful; otherwise return the + * appropriate error. */ Protocols::InteractionModel::Status BoostCommandStarted(uint32_t duration, Optional oneShot, Optional emergencyBoost, Optional temporarySetpoint, diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp index 0568e5c9c7d002..af09a13a0dab2d 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmDelegateImpl.cpp @@ -50,12 +50,12 @@ void WaterHeaterManagementDelegate::SetWhmManufacturer(WhmManufacturer & whmManu * *********************************************************************************/ -BitMask WaterHeaterManagementDelegate::GetHeaterTypes() +BitMask WaterHeaterManagementDelegate::GetHeaterTypes() { return mHeaterTypes; } -BitMask WaterHeaterManagementDelegate::GetHeatDemand() +BitMask WaterHeaterManagementDelegate::GetHeatDemand() { return mHeatDemand; } @@ -80,7 +80,7 @@ BoostStateEnum WaterHeaterManagementDelegate::GetBoostState() return mBoostState; } -void WaterHeaterManagementDelegate::SetHeaterTypes(BitMask heaterTypes) +void WaterHeaterManagementDelegate::SetHeaterTypes(BitMask heaterTypes) { if (mHeaterTypes != heaterTypes) { @@ -90,7 +90,7 @@ void WaterHeaterManagementDelegate::SetHeaterTypes(BitMask heatDemand) +void WaterHeaterManagementDelegate::SetHeatDemand(BitMask heatDemand) { if (mHeatDemand != heatDemand) { diff --git a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp index dd8452a5c816ca..8865501fcca6fe 100644 --- a/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp +++ b/examples/all-clusters-app/all-clusters-common/src/WhmManufacturer.cpp @@ -43,8 +43,8 @@ CHIP_ERROR WhmManufacturer::Init() return CHIP_ERROR_UNINITIALIZED; } - dg->SetHeaterTypes(BitMask(WaterHeaterTypeBitmap::kImmersionElement1)); - dg->SetHeatDemand(BitMask(WaterHeaterDemandBitmap::kImmersionElement1)); + dg->SetHeaterTypes(BitMask(WaterHeaterHeatSourceBitmap::kImmersionElement1)); + dg->SetHeatDemand(BitMask(WaterHeaterHeatSourceBitmap::kImmersionElement1)); dg->SetEstimatedHeatRequired(10000); return CHIP_NO_ERROR; @@ -55,36 +55,36 @@ CHIP_ERROR WhmManufacturer::Shutdown() return CHIP_NO_ERROR; } -BitMask WhmManufacturer::DetermineHeatingSources() +BitMask WhmManufacturer::DetermineHeatingSources() { WaterHeaterManagementDelegate * dg = GetWhmManufacturer()->GetWhmDelegate(); if (dg == nullptr) { ChipLogError(AppServer, "WhmDelegate is not initialized"); - return BitMask(0); + return BitMask(0); } // A list of valid heaterTypes uint8_t waterHeaterTypeValues[] = { - static_cast(WaterHeaterTypeBitmap::kImmersionElement1), - static_cast(WaterHeaterTypeBitmap::kImmersionElement2), - static_cast(WaterHeaterTypeBitmap::kHeatPump), - static_cast(WaterHeaterTypeBitmap::kBoiler), - static_cast(WaterHeaterTypeBitmap::kOther), + static_cast(WaterHeaterHeatSourceBitmap::kImmersionElement1), + static_cast(WaterHeaterHeatSourceBitmap::kImmersionElement2), + static_cast(WaterHeaterHeatSourceBitmap::kHeatPump), + static_cast(WaterHeaterHeatSourceBitmap::kBoiler), + static_cast(WaterHeaterHeatSourceBitmap::kOther), }; // The corresponding list of valid headerDemands uint8_t waterHeaterDemandValues[] = { - static_cast(WaterHeaterDemandBitmap::kImmersionElement1), - static_cast(WaterHeaterDemandBitmap::kImmersionElement2), - static_cast(WaterHeaterDemandBitmap::kHeatPump), - static_cast(WaterHeaterDemandBitmap::kBoiler), - static_cast(WaterHeaterDemandBitmap::kOther), + static_cast(WaterHeaterHeatSourceBitmap::kImmersionElement1), + static_cast(WaterHeaterHeatSourceBitmap::kImmersionElement2), + static_cast(WaterHeaterHeatSourceBitmap::kHeatPump), + static_cast(WaterHeaterHeatSourceBitmap::kBoiler), + static_cast(WaterHeaterHeatSourceBitmap::kOther), }; // Iterate across the valid waterHeaterTypes seeing which heating sources are available based on heaterTypes. // Set the corresponding bit in the heaterDemand bitmap. - BitMask heaterTypes = dg->GetHeaterTypes(); + BitMask heaterTypes = dg->GetHeaterTypes(); uint8_t heaterDemandMask = 0; for (uint16_t idx = 0; idx < static_cast(sizeof(waterHeaterTypeValues) / sizeof(waterHeaterTypeValues[0])); idx++) @@ -96,7 +96,7 @@ BitMask WhmManufacturer::DetermineHeatingSources() } } - return BitMask(heaterDemandMask); + return BitMask(heaterDemandMask); } Status WhmManufacturer::TurnHeatingOn(bool emergencyBoost) @@ -111,12 +111,12 @@ Status WhmManufacturer::TurnHeatingOn(bool emergencyBoost) { // emergencyBoost that the consumer wants the water to be heated as quickly as practicable. // Thus, cause multiple heat sources to be activated - dg->SetHeatDemand(BitMask(WaterHeaterDemandBitmap::kImmersionElement1, - WaterHeaterDemandBitmap::kImmersionElement2)); + dg->SetHeatDemand(BitMask(WaterHeaterHeatSourceBitmap::kImmersionElement1, + WaterHeaterHeatSourceBitmap::kImmersionElement2)); } else { - dg->SetHeatDemand(BitMask(WaterHeaterDemandBitmap::kImmersionElement1)); + dg->SetHeatDemand(BitMask(WaterHeaterHeatSourceBitmap::kImmersionElement1)); } return status; @@ -130,7 +130,7 @@ Status WhmManufacturer::TurnHeatingOff() WaterHeaterManagementDelegate * dg = GetWhmDelegate(); - dg->SetHeatDemand(BitMask(0)); + dg->SetHeatDemand(BitMask(0)); return status; } @@ -167,7 +167,7 @@ void SetTestEventTrigger_BasicInstallationTestEvent() // Simulate installation in a 100L tank full of water at 20C, with a target temperature of 60C, in OFF mode dg->SetTankVolume(100); dg->SetTargetWaterTemperature(6000); - dg->SetHeaterTypes(BitMask(WaterHeaterTypeBitmap::kImmersionElement1)); + dg->SetHeaterTypes(BitMask(WaterHeaterHeatSourceBitmap::kImmersionElement1)); dg->DrawOffHotWater(100, 2000); } diff --git a/examples/fabric-admin/README.md b/examples/fabric-admin/README.md index da2a4d6b21684e..7178ab2c2f8c94 100644 --- a/examples/fabric-admin/README.md +++ b/examples/fabric-admin/README.md @@ -14,7 +14,7 @@ fabrics. For Linux host example: ``` -./scripts/examples/gn_build_example.sh examples/fabric-admin out/debug/standalone chip_config_network_layer_ble=false 'import("//with_pw_rpc.gni")' +./scripts/examples/gn_build_example.sh examples/fabric-admin out/debug/standalone 'import("//with_pw_rpc.gni")' ``` For Raspberry Pi 4 example: diff --git a/examples/fabric-admin/commands/pairing/DeviceSynchronization.h b/examples/fabric-admin/commands/pairing/DeviceSynchronization.h index 16c0e264928639..6aca23f31cbeaa 100644 --- a/examples/fabric-admin/commands/pairing/DeviceSynchronization.h +++ b/examples/fabric-admin/commands/pairing/DeviceSynchronization.h @@ -26,7 +26,7 @@ #include "fabric_bridge_service/fabric_bridge_service.pb.h" #include "fabric_bridge_service/fabric_bridge_service.rpc.pb.h" -/// Ensures that device data is synchronized to the remove fabric bridge. +/// Ensures that device data is synchronized to the remote fabric bridge. /// /// Includes a state machine that: /// - initiates a "read basic information data" command to fetch basic information diff --git a/examples/fabric-bridge-app/fabric-bridge-common/include/BridgedDevice.h b/examples/fabric-bridge-app/fabric-bridge-common/include/BridgedDevice.h index a65ddb15a736f5..3dab8d3b16b2d6 100644 --- a/examples/fabric-bridge-app/fabric-bridge-common/include/BridgedDevice.h +++ b/examples/fabric-bridge-app/fabric-bridge-common/include/BridgedDevice.h @@ -43,11 +43,13 @@ class BridgedDevice BridgedDevice(chip::NodeId nodeId); virtual ~BridgedDevice() = default; + [[nodiscard]] bool IsReachable() const { return mReachable; } + void SetReachable(bool reachable); + void LogActiveChangeEvent(uint32_t promisedActiveDurationMs); - bool IsReachable(); - bool IsIcd(); - void SetReachable(bool reachable); + [[nodiscard]] bool IsIcd() const { return mIsIcd; } + void SetIcd(bool icd) { mIsIcd = icd; } inline void SetEndpointId(chip::EndpointId id) { mEndpointId = id; }; inline chip::EndpointId GetEndpointId() { return mEndpointId; }; @@ -63,11 +65,12 @@ class BridgedDevice void SetUniqueId(const std::string & value) { mAttributes.uniqueId = value; } protected: - bool mReachable; - bool mIsIcd = false; - chip::NodeId mNodeId; - chip::EndpointId mEndpointId; - chip::EndpointId mParentEndpointId; + bool mReachable = false; + bool mIsIcd = false; + + chip::NodeId mNodeId = 0; + chip::EndpointId mEndpointId = 0; + chip::EndpointId mParentEndpointId = 0; BridgedAttributes mAttributes; }; diff --git a/examples/fabric-bridge-app/fabric-bridge-common/src/BridgedDevice.cpp b/examples/fabric-bridge-app/fabric-bridge-common/src/BridgedDevice.cpp index a5e041b487d962..21f19189afefe6 100644 --- a/examples/fabric-bridge-app/fabric-bridge-common/src/BridgedDevice.cpp +++ b/examples/fabric-bridge-app/fabric-bridge-common/src/BridgedDevice.cpp @@ -19,7 +19,6 @@ #include "BridgedDevice.h" #include -#include #include #include @@ -32,7 +31,7 @@ struct ActiveChangeEventWorkData uint32_t mPromisedActiveDuration; }; -static void ActiveChangeEventWork(intptr_t arg) +void ActiveChangeEventWork(intptr_t arg) { ActiveChangeEventWorkData * data = reinterpret_cast(arg); @@ -68,16 +67,6 @@ void BridgedDevice::LogActiveChangeEvent(uint32_t promisedActiveDurationMs) chip::DeviceLayer::PlatformMgr().ScheduleWork(ActiveChangeEventWork, reinterpret_cast(workdata)); } -bool BridgedDevice::IsReachable() -{ - return mReachable; -} - -bool BridgedDevice::IsIcd() -{ - return mIsIcd; -} - void BridgedDevice::SetReachable(bool reachable) { mReachable = reachable; diff --git a/examples/fabric-bridge-app/fabric-bridge-common/src/BridgedDeviceBasicInformationImpl.cpp b/examples/fabric-bridge-app/fabric-bridge-common/src/BridgedDeviceBasicInformationImpl.cpp index c088935df30c59..7fa48a4a158ddd 100644 --- a/examples/fabric-bridge-app/fabric-bridge-common/src/BridgedDeviceBasicInformationImpl.cpp +++ b/examples/fabric-bridge-app/fabric-bridge-common/src/BridgedDeviceBasicInformationImpl.cpp @@ -24,7 +24,6 @@ #include static constexpr unsigned kBridgedDeviceBasicInformationClusterRevision = 4; -static constexpr unsigned kBridgedDeviceBasicInformationFeatureMap = 0; using namespace ::chip; using namespace ::chip::app; @@ -49,9 +48,12 @@ CHIP_ERROR BridgedDeviceBasicInformationImpl::Read(const ConcreteReadAttributePa case BasicInformation::Attributes::ClusterRevision::Id: encoder.Encode(kBridgedDeviceBasicInformationClusterRevision); break; - case BasicInformation::Attributes::FeatureMap::Id: - encoder.Encode(kBridgedDeviceBasicInformationFeatureMap); - break; + case BasicInformation::Attributes::FeatureMap::Id: { + BitMask features; + features.Set(Clusters::BridgedDeviceBasicInformation::Feature::kBridgedICDSupport, dev->IsIcd()); + encoder.Encode(features); + } + break; case BasicInformation::Attributes::UniqueID::Id: encoder.Encode(CharSpan::fromCharString(dev->GetBridgedAttributes().uniqueId.c_str())); break; diff --git a/examples/fabric-bridge-app/fabric-bridge-common/src/BridgedDeviceManager.cpp b/examples/fabric-bridge-app/fabric-bridge-common/src/BridgedDeviceManager.cpp index b361e6667278a4..8db44c3e8bfd9b 100644 --- a/examples/fabric-bridge-app/fabric-bridge-common/src/BridgedDeviceManager.cpp +++ b/examples/fabric-bridge-app/fabric-bridge-common/src/BridgedDeviceManager.cpp @@ -141,17 +141,31 @@ constexpr CommandId administratorCommissioningCommands[] = { kInvalidCommandId, }; +// clang-format off // Declare Cluster List for Bridged Node endpoint DECLARE_DYNAMIC_CLUSTER_LIST_BEGIN(bridgedNodeClusters) -DECLARE_DYNAMIC_CLUSTER(Descriptor::Id, descriptorAttrs, ZAP_CLUSTER_MASK(SERVER), nullptr, nullptr), + DECLARE_DYNAMIC_CLUSTER(Descriptor::Id, descriptorAttrs, ZAP_CLUSTER_MASK(SERVER), nullptr, nullptr), + DECLARE_DYNAMIC_CLUSTER(BridgedDeviceBasicInformation::Id, bridgedDeviceBasicAttrs, ZAP_CLUSTER_MASK(SERVER), nullptr, nullptr), + DECLARE_DYNAMIC_CLUSTER(EcosystemInformation::Id, ecosystemInformationBasicAttrs, ZAP_CLUSTER_MASK(SERVER), nullptr, nullptr), + DECLARE_DYNAMIC_CLUSTER(AdministratorCommissioning::Id, AdministratorCommissioningAttrs, ZAP_CLUSTER_MASK(SERVER), + administratorCommissioningCommands, nullptr) +DECLARE_DYNAMIC_CLUSTER_LIST_END; + +DECLARE_DYNAMIC_CLUSTER_LIST_BEGIN(icdBridgedNodeClusters) + DECLARE_DYNAMIC_CLUSTER(Descriptor::Id, descriptorAttrs, ZAP_CLUSTER_MASK(SERVER), nullptr, nullptr), DECLARE_DYNAMIC_CLUSTER(BridgedDeviceBasicInformation::Id, bridgedDeviceBasicAttrs, ZAP_CLUSTER_MASK(SERVER), bridgedDeviceBasicInformationCommands, nullptr), - DECLARE_DYNAMIC_CLUSTER(EcosystemInformation::Id, ecosystemInformationBasicAttrs, ZAP_CLUSTER_MASK(SERVER), nullptr, nullptr), DECLARE_DYNAMIC_CLUSTER(AdministratorCommissioning::Id, AdministratorCommissioningAttrs, ZAP_CLUSTER_MASK(SERVER), - administratorCommissioningCommands, nullptr) DECLARE_DYNAMIC_CLUSTER_LIST_END; + administratorCommissioningCommands, nullptr) +DECLARE_DYNAMIC_CLUSTER_LIST_END; +// clang-format on // Declare Bridged Node endpoint DECLARE_DYNAMIC_ENDPOINT(sBridgedNodeEndpoint, bridgedNodeClusters); +DECLARE_DYNAMIC_ENDPOINT(sIcdBridgedNodeEndpoint, icdBridgedNodeClusters); + +// TODO: this is a single version array, however we may have many +// different clusters that are independent. DataVersion sBridgedNodeDataVersions[ArraySize(bridgedNodeClusters)]; const EmberAfDeviceType sBridgedDeviceTypes[] = { { DEVICE_TYPE_BRIDGED_NODE, DEVICE_VERSION_DEFAULT } }; @@ -171,9 +185,12 @@ void BridgedDeviceManager::Init() std::optional BridgedDeviceManager::AddDeviceEndpoint(std::unique_ptr dev, chip::EndpointId parentEndpointId) { - EmberAfEndpointType * ep = &sBridgedNodeEndpoint; + EmberAfEndpointType * ep = dev->IsIcd() ? &sIcdBridgedNodeEndpoint : &sBridgedNodeEndpoint; + const chip::Span & deviceTypeList = Span(sBridgedDeviceTypes); - const chip::Span & dataVersionStorage = Span(sBridgedNodeDataVersions); + + // TODO: this shares data version among different clusters, which seems incorrect + const chip::Span & dataVersionStorage = Span(sBridgedNodeDataVersions); if (dev->GetBridgedAttributes().uniqueId.empty()) { diff --git a/examples/fabric-bridge-app/linux/RpcServer.cpp b/examples/fabric-bridge-app/linux/RpcServer.cpp index 8838360ee7b1e7..bae007ed484935 100644 --- a/examples/fabric-bridge-app/linux/RpcServer.cpp +++ b/examples/fabric-bridge-app/linux/RpcServer.cpp @@ -109,6 +109,7 @@ pw::Status FabricBridge::AddSynchronizedDevice(const chip_rpc_SynchronizedDevice } device->SetBridgedAttributes(attributes); + device->SetIcd(request.has_is_icd && request.is_icd); auto result = BridgeDeviceMgr().AddDeviceEndpoint(std::move(device), 1 /* parentEndpointId */); if (!result.has_value()) @@ -117,8 +118,11 @@ pw::Status FabricBridge::AddSynchronizedDevice(const chip_rpc_SynchronizedDevice return pw::Status::Unknown(); } + BridgedDevice * addedDevice = BridgeDeviceMgr().GetDeviceByNodeId(nodeId); + VerifyOrDie(addedDevice); + CHIP_ERROR err = EcosystemInformation::EcosystemInformationServer::Instance().AddEcosystemInformationClusterToEndpoint( - device->GetEndpointId()); + addedDevice->GetEndpointId()); VerifyOrDie(err == CHIP_NO_ERROR); return pw::OkStatus(); diff --git a/examples/network-manager-app/linux/tbrm.cpp b/examples/network-manager-app/linux/tbrm.cpp index 908666ee61d8a0..eada060526add7 100644 --- a/examples/network-manager-app/linux/tbrm.cpp +++ b/examples/network-manager-app/linux/tbrm.cpp @@ -94,15 +94,23 @@ class FakeBorderRouterDelegate final : public ThreadBorderRouterManagement::Dele mActivateDatasetCallback = callback; mActivateDatasetSequence = sequenceNum; - DeviceLayer::SystemLayer().StartTimer(System::Clock::Milliseconds32(3000), CompleteDatasetActivation, this); + DeviceLayer::SystemLayer().StartTimer(System::Clock::Milliseconds32(1000), ActivateActiveDataset, this); } CHIP_ERROR CommitActiveDataset() override { return CHIP_NO_ERROR; } CHIP_ERROR RevertActiveDataset() override { return CHIP_ERROR_NOT_IMPLEMENTED; } - CHIP_ERROR SetPendingDataset(const Thread::OperationalDataset & pendingDataset) override { return CHIP_ERROR_NOT_IMPLEMENTED; } + + CHIP_ERROR SetPendingDataset(const Thread::OperationalDataset & pendingDataset) override + { + ReturnErrorOnFailure(mPendingDataset.Init(pendingDataset.AsByteSpan())); + uint32_t delayTimerMillis; + ReturnErrorOnFailure(mPendingDataset.GetDelayTimer(delayTimerMillis)); + DeviceLayer::SystemLayer().StartTimer(System::Clock::Milliseconds32(delayTimerMillis), ActivatePendingDataset, this); + return CHIP_NO_ERROR; + } private: - static void CompleteDatasetActivation(System::Layer *, void * context) + static void ActivateActiveDataset(System::Layer *, void * context) { auto * self = static_cast(context); auto * callback = self->mActivateDatasetCallback; @@ -111,6 +119,18 @@ class FakeBorderRouterDelegate final : public ThreadBorderRouterManagement::Dele callback->OnActivateDatasetComplete(sequenceNum, CHIP_NO_ERROR); } + static void ActivatePendingDataset(System::Layer *, void * context) + { + auto * self = static_cast(context); + self->mActiveDataset.Init(self->mPendingDataset.AsByteSpan()); + self->mPendingDataset.Clear(); + // This could just call MatterReportingAttributeChangeCallback directly + self->mAttributeChangeCallback->ReportAttributeChanged( + ThreadBorderRouterManagement::Attributes::ActiveDatasetTimestamp::Id); + self->mAttributeChangeCallback->ReportAttributeChanged( + ThreadBorderRouterManagement::Attributes::PendingDatasetTimestamp::Id); + } + AttributeChangeCallback * mAttributeChangeCallback; Thread::OperationalDataset mActiveDataset; Thread::OperationalDataset mPendingDataset; diff --git a/examples/network-manager-app/network-manager-common/network-manager-app.matter b/examples/network-manager-app/network-manager-common/network-manager-app.matter index 3f24e4be5744c6..627838288db0d1 100644 --- a/examples/network-manager-app/network-manager-common/network-manager-app.matter +++ b/examples/network-manager-app/network-manager-common/network-manager-app.matter @@ -1892,6 +1892,7 @@ endpoint 1 { handle command GetPendingDatasetRequest; handle command DatasetResponse; handle command SetActiveDatasetRequest; + handle command SetPendingDatasetRequest; } server cluster ThreadNetworkDirectory { diff --git a/examples/network-manager-app/network-manager-common/network-manager-app.zap b/examples/network-manager-app/network-manager-common/network-manager-app.zap index 7c1445ac46c190..1d27e3c346f320 100644 --- a/examples/network-manager-app/network-manager-common/network-manager-app.zap +++ b/examples/network-manager-app/network-manager-common/network-manager-app.zap @@ -3407,6 +3407,14 @@ "source": "client", "isIncoming": 1, "isEnabled": 1 + }, + { + "name": "SetPendingDatasetRequest", + "code": 4, + "mfgCode": null, + "source": "client", + "isIncoming": 1, + "isEnabled": 1 } ], "attributes": [ @@ -3500,7 +3508,7 @@ "storageOption": "External", "singleton": 0, "bounded": 0, - "defaultValue": "", + "defaultValue": null, "reportable": 1, "minInterval": 1, "maxInterval": 65534, diff --git a/examples/rvc-app/rvc-common/include/rvc-service-area-delegate.h b/examples/rvc-app/rvc-common/include/rvc-service-area-delegate.h index 759c22a4e609cb..5397e3096c14ae 100644 --- a/examples/rvc-app/rvc-common/include/rvc-service-area-delegate.h +++ b/examples/rvc-app/rvc-common/include/rvc-service-area-delegate.h @@ -47,25 +47,24 @@ class RvcServiceAreaDelegate : public Delegate bool IsSetSelectedAreasAllowed(MutableCharSpan statusText) override; bool IsValidSelectAreasSet(const ServiceArea::Commands::SelectAreas::DecodableType & req, - ServiceArea::SelectAreasStatus & locationStatus, MutableCharSpan statusText) override; + ServiceArea::SelectAreasStatus & areaStatus, MutableCharSpan statusText) override; bool HandleSkipCurrentArea(uint32_t skippedArea, MutableCharSpan skipStatusText) override; //************************************************************************* - // Supported Locations accessors + // Supported Areas accessors bool IsSupportedAreasChangeAllowed() override; uint32_t GetNumberOfSupportedAreas() override; - bool GetSupportedLocationByIndex(uint32_t listIndex, ServiceArea::AreaStructureWrapper & supportedLocation) override; + bool GetSupportedAreaByIndex(uint32_t listIndex, AreaStructureWrapper & supportedArea) override; - bool GetSupportedLocationById(uint32_t aAreaId, uint32_t & listIndex, - ServiceArea::AreaStructureWrapper & supportedLocation) override; + bool GetSupportedAreaById(uint32_t aAreaId, uint32_t & listIndex, AreaStructureWrapper & supportedArea) override; - bool AddSupportedLocation(const ServiceArea::AreaStructureWrapper & newArea, uint32_t & listIndex) override; + bool AddSupportedArea(const AreaStructureWrapper & newArea, uint32_t & listIndex) override; - bool ModifySupportedLocation(uint32_t listIndex, const ServiceArea::AreaStructureWrapper & modifiedLocation) override; + bool ModifySupportedArea(uint32_t listIndex, const AreaStructureWrapper & modifiedArea) override; bool ClearSupportedAreas() override; @@ -87,15 +86,13 @@ class RvcServiceAreaDelegate : public Delegate bool ClearSupportedMaps() override; //************************************************************************* - // Selected Locations accessors + // Selected Areas accessors uint32_t GetNumberOfSelectedAreas() override; - bool GetSelectedLocationByIndex(uint32_t listIndex, uint32_t & selectedLocation) override; + bool GetSelectedAreaByIndex(uint32_t listIndex, uint32_t & selectedArea) override; - // IsSelectedLocation() no override - - bool AddSelectedLocation(uint32_t aAreaId, uint32_t & listIndex) override; + bool AddSelectedArea(uint32_t aAreaId, uint32_t & listIndex) override; bool ClearSelectedAreas() override; diff --git a/examples/rvc-app/rvc-common/src/rvc-service-area-delegate.cpp b/examples/rvc-app/rvc-common/src/rvc-service-area-delegate.cpp index aca96da836151d..1bc60baa0774cd 100644 --- a/examples/rvc-app/rvc-common/src/rvc-service-area-delegate.cpp +++ b/examples/rvc-app/rvc-common/src/rvc-service-area-delegate.cpp @@ -32,37 +32,43 @@ CHIP_ERROR RvcServiceAreaDelegate::Init() GetInstance()->AddSupportedMap(supportedMapId_XX, "My Map XX"_span); GetInstance()->AddSupportedMap(supportedMapId_YY, "My Map YY"_span); - // hardcoded fill of SUPPORTED LOCATIONS for prototyping + // hardcoded fill of SUPPORTED AREAS for prototyping uint32_t supportedAreaID_A = 7; uint32_t supportedAreaID_B = 1234567; uint32_t supportedAreaID_C = 10050; uint32_t supportedAreaID_D = 0x88888888; - // Location A has name, floor number, uses map XX - GetInstance()->AddSupportedLocation(supportedAreaID_A, DataModel::Nullable(supportedMapId_XX), "My Location A"_span, - DataModel::Nullable(4), DataModel::Nullable(), - DataModel::Nullable(), - DataModel::Nullable()); - - // Location B has name, uses map XX - GetInstance()->AddSupportedLocation(supportedAreaID_B, DataModel::Nullable(supportedMapId_XX), "My Location B"_span, - DataModel::Nullable(), DataModel::Nullable(), - DataModel::Nullable(), - DataModel::Nullable()); - - // Location C has full SemData, no name, Map YY - GetInstance()->AddSupportedLocation(supportedAreaID_C, DataModel::Nullable(supportedMapId_YY), CharSpan(), - DataModel::Nullable(-1), - DataModel::Nullable(Globals::AreaTypeTag::kPlayRoom), - DataModel::Nullable(Globals::LandmarkTag::kBackDoor), - DataModel::Nullable(Globals::RelativePositionTag::kNextTo)); - - // Location D has null values for all HomeLocationStruct fields, Map YY - GetInstance()->AddSupportedLocation(supportedAreaID_D, DataModel::Nullable(supportedMapId_YY), "My Location D"_span, - DataModel::Nullable(), DataModel::Nullable(), - DataModel::Nullable(Globals::LandmarkTag::kCouch), - DataModel::Nullable(Globals::RelativePositionTag::kNextTo)); - + // Area A has name, floor number, uses map XX + auto areaA = + AreaStructureWrapper{} + .SetAreaId(supportedAreaID_A) + .SetMapId(supportedMapId_XX) + .SetLocationInfo("My Location A"_span, DataModel::Nullable(4), DataModel::Nullable()); + + // Area B has name, uses map XX + auto areaB = AreaStructureWrapper{} + .SetMapId(supportedAreaID_B) + .SetMapId(supportedMapId_XX) + .SetLocationInfo("My Location B"_span, DataModel::NullNullable, DataModel::NullNullable); + + // Area C has full SemData, no name, Map YY + auto areaC = AreaStructureWrapper{} + .SetAreaId(supportedAreaID_C) + .SetMapId(supportedMapId_YY) + .SetLocationInfo(""_span, -1, Globals::AreaTypeTag::kPlayRoom) + .SetLandmarkInfo(Globals::LandmarkTag::kBackDoor, Globals::RelativePositionTag::kNextTo); + + // Area D has null values for all landmark fields, Map YY + auto areaD = AreaStructureWrapper{} + .SetAreaId(supportedAreaID_D) + .SetMapId(supportedMapId_YY) + .SetLocationInfo("My Location D"_span, DataModel::NullNullable, DataModel::NullNullable) + .SetLandmarkInfo(Globals::LandmarkTag::kCouch, Globals::RelativePositionTag::kNextTo); + + GetInstance()->AddSupportedArea(areaA); + GetInstance()->AddSupportedArea(areaB); + GetInstance()->AddSupportedArea(areaC); + GetInstance()->AddSupportedArea(areaD); GetInstance()->SetCurrentArea(supportedAreaID_C); return CHIP_NO_ERROR; @@ -77,8 +83,8 @@ bool RvcServiceAreaDelegate::IsSetSelectedAreasAllowed(MutableCharSpan statusTex return true; }; -bool RvcServiceAreaDelegate::IsValidSelectAreasSet(const Commands::SelectAreas::DecodableType & req, - SelectAreasStatus & locationStatus, MutableCharSpan statusText) +bool RvcServiceAreaDelegate::IsValidSelectAreasSet(const Commands::SelectAreas::DecodableType & req, SelectAreasStatus & areaStatus, + MutableCharSpan statusText) { // TODO IMPLEMENT return true; @@ -91,7 +97,7 @@ bool RvcServiceAreaDelegate::HandleSkipCurrentArea(uint32_t skippedArea, Mutable }; //************************************************************************* -// Supported Locations accessors +// Supported Areas accessors bool RvcServiceAreaDelegate::IsSupportedAreasChangeAllowed() { @@ -104,19 +110,18 @@ uint32_t RvcServiceAreaDelegate::GetNumberOfSupportedAreas() return static_cast(mSupportedAreas.size()); } -bool RvcServiceAreaDelegate::GetSupportedLocationByIndex(uint32_t listIndex, AreaStructureWrapper & aSupportedLocation) +bool RvcServiceAreaDelegate::GetSupportedAreaByIndex(uint32_t listIndex, AreaStructureWrapper & supportedArea) { if (listIndex < mSupportedAreas.size()) { - aSupportedLocation = mSupportedAreas[listIndex]; + supportedArea = mSupportedAreas[listIndex]; return true; } return false; }; -bool RvcServiceAreaDelegate::GetSupportedLocationById(uint32_t aAreaID, uint32_t & listIndex, - AreaStructureWrapper & aSupportedLocation) +bool RvcServiceAreaDelegate::GetSupportedAreaById(uint32_t aAreaID, uint32_t & listIndex, AreaStructureWrapper & supportedArea) { // We do not need to reimplement this method as it's already done by the SDK. // We are reimplementing this method, still using linear search, but with some optimization on the SDK implementation @@ -127,7 +132,7 @@ bool RvcServiceAreaDelegate::GetSupportedLocationById(uint32_t aAreaID, uint32_t { if (mSupportedAreas[listIndex].areaID == aAreaID) { - aSupportedLocation = mSupportedAreas[listIndex]; + supportedArea = mSupportedAreas[listIndex]; return true; } @@ -137,7 +142,7 @@ bool RvcServiceAreaDelegate::GetSupportedLocationById(uint32_t aAreaID, uint32_t return false; }; -bool RvcServiceAreaDelegate::AddSupportedLocation(const AreaStructureWrapper & newArea, uint32_t & listIndex) +bool RvcServiceAreaDelegate::AddSupportedArea(const AreaStructureWrapper & newArea, uint32_t & listIndex) { // The server instance (caller) is responsible for ensuring that there are no duplicate area IDs, list size not exceeded, // etc. @@ -145,33 +150,33 @@ bool RvcServiceAreaDelegate::AddSupportedLocation(const AreaStructureWrapper & n // Double-check list size to ensure there no memory issues. if (mSupportedAreas.size() < kMaxNumSupportedAreas) { - // not sorting list, number of locations normally expected to be small, max 255 + // not sorting list, number of areas normally expected to be small, max 255 mSupportedAreas.push_back(newArea); listIndex = static_cast(mSupportedMaps.size()) - 1; // new element is last in list return true; } - ChipLogError(Zcl, "AddSupportedLocation %u - supported locations list is already at maximum size %u", newArea.areaID, + ChipLogError(Zcl, "AddSupportedArea %u - supported areas list is already at maximum size %u", newArea.areaID, static_cast(kMaxNumSupportedAreas)); return false; } -bool RvcServiceAreaDelegate::ModifySupportedLocation(uint32_t listIndex, const AreaStructureWrapper & modifiedLocation) +bool RvcServiceAreaDelegate::ModifySupportedArea(uint32_t listIndex, const AreaStructureWrapper & modifiedArea) { // The server instance (caller) is responsible for ensuring that there are no duplicate area IDs, list size not exceeded, // etc. // Double-check that areaID's match. - if (modifiedLocation.areaID != mSupportedAreas[listIndex].areaID) + if (modifiedArea.areaID != mSupportedAreas[listIndex].areaID) { - ChipLogError(Zcl, "ModifySupportedLocation - areaID's do not match, new areaID %u, existing areaID %u", - modifiedLocation.areaID, mSupportedAreas[listIndex].areaID); + ChipLogError(Zcl, "ModifySupportedArea - areaID's do not match, new areaID %u, existing areaID %u", modifiedArea.areaID, + mSupportedAreas[listIndex].areaID); return false; } // checks passed, update the attribute - mSupportedAreas[listIndex] = modifiedLocation; + mSupportedAreas[listIndex] = modifiedArea; return true; } @@ -240,7 +245,7 @@ bool RvcServiceAreaDelegate::AddSupportedMap(const MapStructureWrapper & newMap, // Double-check list size to ensure there no memory issues. if (mSupportedMaps.size() < kMaxNumSupportedMaps) { - // not sorting list, number of locations normally expected to be small, max 255 + // not sorting list, number of areas normally expected to be small, max 255 mSupportedMaps.push_back(newMap); listIndex = static_cast(mSupportedMaps.size()) - 1; // new element is last in list return true; @@ -281,25 +286,25 @@ bool RvcServiceAreaDelegate::ClearSupportedMaps() } //************************************************************************* -// Selected Locations accessors +// Selected areas accessors uint32_t RvcServiceAreaDelegate::GetNumberOfSelectedAreas() { return static_cast(mSelectedAreas.size()); } -bool RvcServiceAreaDelegate::GetSelectedLocationByIndex(uint32_t listIndex, uint32_t & aSelectedLocation) +bool RvcServiceAreaDelegate::GetSelectedAreaByIndex(uint32_t listIndex, uint32_t & selectedArea) { if (listIndex < mSelectedAreas.size()) { - aSelectedLocation = mSelectedAreas[listIndex]; + selectedArea = mSelectedAreas[listIndex]; return true; } return false; }; -bool RvcServiceAreaDelegate::AddSelectedLocation(uint32_t aAreaID, uint32_t & listIndex) +bool RvcServiceAreaDelegate::AddSelectedArea(uint32_t aAreaID, uint32_t & listIndex) { // The server instance (caller) is responsible for ensuring that there are no duplicate area IDs, list size not exceeded, // etc. @@ -307,12 +312,12 @@ bool RvcServiceAreaDelegate::AddSelectedLocation(uint32_t aAreaID, uint32_t & li // Double-check list size to ensure there no memory issues. if (mSelectedAreas.size() < kMaxNumSelectedAreas) { - // not sorting list, number of locations normally expected to be small, max 255 + // not sorting list, number of areas normally expected to be small, max 255 mSelectedAreas.push_back(aAreaID); listIndex = static_cast(mSelectedAreas.size()) - 1; // new element is last in list return true; } - ChipLogError(Zcl, "AddSelectedLocation %u - selected locations list is already at maximum size %u", aAreaID, + ChipLogError(Zcl, "AddSelectedArea %u - selected areas list is already at maximum size %u", aAreaID, static_cast(kMaxNumSelectedAreas)); return false; @@ -378,7 +383,7 @@ bool RvcServiceAreaDelegate::AddProgressElement(const Structs::ProgressStruct::T // Double-check list size to ensure there no memory issues. if (mProgressList.size() < kMaxNumProgressElements) { - // not sorting list, number of locations normally expected to be small, max 255 + // not sorting list, number of areas normally expected to be small, max 255 mProgressList.push_back(newProgressElement); listIndex = static_cast(mProgressList.size()) - 1; // new element is last in list return true; diff --git a/examples/thermostat/linux/include/thermostat-delegate-impl.h b/examples/thermostat/linux/include/thermostat-delegate-impl.h index f559977f341949..8252f2274f9d79 100644 --- a/examples/thermostat/linux/include/thermostat-delegate-impl.h +++ b/examples/thermostat/linux/include/thermostat-delegate-impl.h @@ -44,6 +44,10 @@ class ThermostatDelegate : public Delegate public: static inline ThermostatDelegate & GetInstance() { return sInstance; } + std::optional + GetAtomicWriteTimeout(DataModel::DecodableList attributeRequests, + System::Clock::Milliseconds16 timeoutRequest) override; + CHIP_ERROR GetPresetTypeAtIndex(size_t index, Structs::PresetTypeStruct::Type & presetType) override; uint8_t GetNumberOfPresets() override; diff --git a/examples/thermostat/linux/thermostat-delegate-impl.cpp b/examples/thermostat/linux/thermostat-delegate-impl.cpp index 61d496f233b408..c39a757a8b0644 100644 --- a/examples/thermostat/linux/thermostat-delegate-impl.cpp +++ b/examples/thermostat/linux/thermostat-delegate-impl.cpp @@ -148,6 +148,47 @@ CHIP_ERROR ThermostatDelegate::SetActivePresetHandle(const DataModel::Nullable +ThermostatDelegate::GetAtomicWriteTimeout(DataModel::DecodableList attributeRequests, + System::Clock::Milliseconds16 timeoutRequest) +{ + auto attributeIdsIter = attributeRequests.begin(); + bool requestedPresets = false, requestedSchedules = false; + while (attributeIdsIter.Next()) + { + auto & attributeId = attributeIdsIter.GetValue(); + + switch (attributeId) + { + case Attributes::Presets::Id: + requestedPresets = true; + break; + case Attributes::Schedules::Id: + requestedSchedules = true; + break; + default: + return System::Clock::Milliseconds16(0); + } + } + if (attributeIdsIter.GetStatus() != CHIP_NO_ERROR) + { + return System::Clock::Milliseconds16(0); + } + auto timeout = System::Clock::Milliseconds16(0); + if (requestedPresets) + { + // If the client expects to edit the presets, then we'll give it 3 seconds to do so + timeout += std::chrono::milliseconds(3000); + } + if (requestedSchedules) + { + // If the client expects to edit the schedules, then we'll give it 9 seconds to do so + timeout += std::chrono::milliseconds(9000); + } + // If the client requested an even smaller timeout, then use that one + return std::min(timeoutRequest, timeout); +} + void ThermostatDelegate::InitializePendingPresets() { mNextFreeIndexInPendingPresetsList = 0; diff --git a/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h b/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h index bb4e1a77c14f59..3146dd902ffb68 100644 --- a/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h +++ b/scripts/tools/zap/tests/outputs/all-clusters-app/app-templates/endpoint_config.h @@ -2217,6 +2217,7 @@ }; \ const EmberAfGenericClusterFunction chipFuncArrayThermostatServer[] = { \ (EmberAfGenericClusterFunction) emberAfThermostatClusterServerInitCallback, \ + (EmberAfGenericClusterFunction) MatterThermostatClusterServerShutdownCallback, \ (EmberAfGenericClusterFunction) MatterThermostatClusterServerPreAttributeChangedCallback, \ }; \ const EmberAfGenericClusterFunction chipFuncArrayFanControlServer[] = { \ @@ -3755,7 +3756,7 @@ .attributes = ZAP_ATTRIBUTE_INDEX(616), \ .attributeCount = 26, \ .clusterSize = 72, \ - .mask = ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION) | ZAP_CLUSTER_MASK(PRE_ATTRIBUTE_CHANGED_FUNCTION), \ + .mask = ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION) | ZAP_CLUSTER_MASK(SHUTDOWN_FUNCTION) | ZAP_CLUSTER_MASK(PRE_ATTRIBUTE_CHANGED_FUNCTION), \ .functions = chipFuncArrayThermostatServer, \ .acceptedCommandList = ZAP_GENERATED_COMMANDS_INDEX( 241 ), \ .generatedCommandList = ZAP_GENERATED_COMMANDS_INDEX( 246 ), \ diff --git a/src/app/clusters/service-area-server/service-area-cluster-objects.h b/src/app/clusters/service-area-server/service-area-cluster-objects.h index 2d2694e20dbd78..1b040c53cdc1bf 100644 --- a/src/app/clusters/service-area-server/service-area-cluster-objects.h +++ b/src/app/clusters/service-area-server/service-area-cluster-objects.h @@ -27,8 +27,8 @@ namespace Clusters { namespace ServiceArea { // These limits are defined in the spec. -inline constexpr size_t kLocationNameMaxSize = 128u; -inline constexpr size_t kMapNameMaxSize = 64u; +inline constexpr size_t kAreaNameMaxSize = 128u; +inline constexpr size_t kMapNameMaxSize = 64u; /** * This class is used to wrap the AreaStruct object and provide a more user-friendly interface for the data. @@ -36,35 +36,16 @@ inline constexpr size_t kMapNameMaxSize = 64u; */ struct AreaStructureWrapper : public chip::app::Clusters::ServiceArea::Structs::AreaStruct::Type { - AreaStructureWrapper() - { - Set(0, 0, CharSpan(), DataModel::Nullable(), DataModel::Nullable(), - DataModel::Nullable(), DataModel::Nullable()); - } - /** - * @brief This is a full constructor that initializes the location object with the given values. All values are deep copied. - * @param[in] aAreaID The unique identifier of this location. - * @param[in] aMapId The identifier of the supported map associated with this location. - * @param[in] aLocationName A human readable name for this location (empty string if not used). - * @param[in] aFloorNumber The floor level of this location - use negative values for below ground. - * @param[in] aAreaTypeTag A common namespace Area tag - indicates an association of the location with an indoor or outdoor area - * of a home. - * @param[in] aLandmarkTag A common namespace Landmark tag - indicates an association of the location with a home landmark. - * @param[in] aRelativePositionTag A common namespace Relative Position tag - indicates the position of the location with - * respect to the landmark. - * - * @note Requirements regarding what combinations of fields and values are valid are not checked by this class. - * @note If aLocationName is larger than kLocationNameMaxSize, it will be truncated. - * @note If aLocationName is an empty string and aFloorNumber and aAreaTypeTag are null, locationInfo will be set to null. + * @brief This is a default constructor that initializes the location object with the following + * values: areaID = 0, mapID = null, locationInfo = null, landmarkInfo = null. */ - AreaStructureWrapper(uint32_t aAreaID, const DataModel::Nullable & aMapId, const CharSpan & aLocationName, - const DataModel::Nullable & aFloorNumber, - const DataModel::Nullable & aAreaTypeTag, - const DataModel::Nullable & aLandmarkTag, - const DataModel::Nullable & aRelativePositionTag) + AreaStructureWrapper() { - Set(aAreaID, aMapId, aLocationName, aFloorNumber, aAreaTypeTag, aLandmarkTag, aRelativePositionTag); + areaID = 0; + mapID = DataModel::NullNullable; + SetLocationInfoNull(); + SetLandmarkInfoNull(); } /** @@ -87,124 +68,117 @@ struct AreaStructureWrapper : public chip::app::Clusters::ServiceArea::Structs:: { areaID = aOther.areaID; mapID = aOther.mapID; + SetLocationInfo(aOther.areaDesc.locationInfo); + SetLandmarkInfo(aOther.areaDesc.landmarkInfo); - if (aOther.areaDesc.locationInfo.IsNull()) - { - areaDesc.locationInfo.SetNull(); - } - else - { - areaDesc.locationInfo.SetNonNull(); + return *this; + } - // deep copy the name. - auto sizeToCopy = std::min(sizeof(mLocationNameBuffer), aOther.areaDesc.locationInfo.Value().locationName.size()); - memcpy(mLocationNameBuffer, aOther.areaDesc.locationInfo.Value().locationName.data(), sizeToCopy); - areaDesc.locationInfo.Value().locationName = CharSpan(mLocationNameBuffer, sizeToCopy); + bool operator==(const AreaStructureWrapper & aOther) const + { + BitMask config = 0; // Do not ignore the AreaID or the MapID. - areaDesc.locationInfo.Value().floorNumber = aOther.areaDesc.locationInfo.Value().floorNumber; - areaDesc.locationInfo.Value().areaType = aOther.areaDesc.locationInfo.Value().areaType; - } + return IsEqual(aOther, config); + } - if (aOther.areaDesc.landmarkInfo.IsNull()) - { - areaDesc.landmarkInfo.SetNull(); - } - else - { - areaDesc.landmarkInfo.SetNonNull(); - areaDesc.landmarkInfo.Value().landmarkTag = aOther.areaDesc.landmarkInfo.Value().landmarkTag; - if (aOther.areaDesc.landmarkInfo.Value().positionTag.IsNull()) - { - areaDesc.landmarkInfo.Value().positionTag.SetNull(); - } - else - { - areaDesc.landmarkInfo.Value().positionTag.SetNonNull(); - areaDesc.landmarkInfo.Value().positionTag.Value() = aOther.areaDesc.landmarkInfo.Value().positionTag.Value(); - } - } + AreaStructureWrapper & SetAreaId(uint32_t aAreaID) + { + areaID = aAreaID; + return *this; + } + AreaStructureWrapper & SetMapId(const DataModel::Nullable & aMapId) + { + mapID = aMapId; + return *this; + } + + AreaStructureWrapper & SetLocationInfoNull() + { + areaDesc.locationInfo.SetNull(); return *this; } /** - * @brief Set all fields of the location object. All values are deep copied. - * @param[in] aAreaID The unique identifier of this location. - * @param[in] aMapId The identifier of the supported map associated with this location. - * @param[in] aLocationName A human readable name for this location (empty string if not used). - * @param[in] aFloorNumber The floor level of this location - use negative values for below ground. - * @param[in] aAreaTypeTag A common namespace Area tag - indicates an association of the location with an indoor or outdoor area - * of a home. - * @param[in] aLandmarkTag A common namespace Landmark tag - indicates an association of the location with a home landmark. - * @param[in] aRelativePositionTag A common namespace Relative Position tag - indicates the position of the location with - * respect to the landmark. - * - * @note Requirements regarding what combinations of fields and values are valid are not checked by this class. - * @note If aLocationName is larger than kLocationNameMaxSize, it will be truncated. - * @note If aLocationName is an empty string and aFloorNumber and aAreaTypeTag are null, locationInfo will be set to null. + * @brief Set the location information. + * @param[in] locationName The name of the location. If the name is larger than kAreaNameMaxSize, it will be truncated. + * @param[in] floorNumber The floor number of the location. + * @param[in] areaType The type of the area. */ - void Set(uint32_t aAreaID, const DataModel::Nullable & aMapId, const CharSpan & aLocationName, - const DataModel::Nullable & aFloorNumber, const DataModel::Nullable & aAreaType, - const DataModel::Nullable & aLandmarkTag, - const DataModel::Nullable & aRelativePositionTag) + AreaStructureWrapper & SetLocationInfo(const CharSpan & locationName, const DataModel::Nullable & floorNumber, + const DataModel::Nullable & areaType) { - areaID = aAreaID; - mapID = aMapId; + areaDesc.locationInfo.SetNonNull(); + // Copy the name + auto sizeToCopy = std::min(sizeof(mAreaNameBuffer), locationName.size()); + memcpy(mAreaNameBuffer, locationName.data(), sizeToCopy); + areaDesc.locationInfo.Value().locationName = CharSpan(mAreaNameBuffer, sizeToCopy); + areaDesc.locationInfo.Value().floorNumber = floorNumber; + areaDesc.locationInfo.Value().areaType = areaType; - // If there is at least one non-null value for locationInfo, add it to the location structure. - if ((!aLocationName.empty()) || (!aFloorNumber.IsNull()) || (!aAreaType.IsNull())) - { - // Create a home location desc structure and fill it in except for the location name. This is done below. - areaDesc.locationInfo.SetNonNull(Globals::Structs::LocationDescriptorStruct::Type()); + return *this; + } - areaDesc.locationInfo.Value().floorNumber = aFloorNumber; - areaDesc.locationInfo.Value().areaType = aAreaType; - } - else + /** + * @brief Set the location information form a LocationDescriptorStruct object. + * + * @note If the locationName is larger than kAreaNameMaxSize, it will be truncated. + */ + AreaStructureWrapper & SetLocationInfo(DataModel::Nullable locationInfo) + { + if (locationInfo.IsNull()) { - areaDesc.locationInfo.SetNull(); + return SetLocationInfoNull(); } - // todo improve this when addressing issue https://github.com/project-chip/connectedhomeip/issues/34519 - if (aLandmarkTag.IsNull()) - { - areaDesc.landmarkInfo.SetNull(); - } - else - { - areaDesc.landmarkInfo.SetNonNull(); - areaDesc.landmarkInfo.Value().landmarkTag = aLandmarkTag.Value(); - if (aRelativePositionTag.IsNull()) - { - areaDesc.landmarkInfo.Value().positionTag.SetNull(); - } - else - { - areaDesc.landmarkInfo.Value().positionTag.SetNonNull(); - areaDesc.landmarkInfo.Value().positionTag.Value() = aRelativePositionTag.Value(); - } - } + return SetLocationInfo(locationInfo.Value().locationName, locationInfo.Value().floorNumber, locationInfo.Value().areaType); + } - // this assumes areaDesc structure was created above, if appropriate - if (!areaDesc.locationInfo.IsNull()) + AreaStructureWrapper & SetLandmarkInfoNull() + { + areaDesc.landmarkInfo.SetNull(); + return *this; + } + + /** + * @brief Set the landmark information. + * @param[in] landmarkTag The landmark tag. + * @param[in] relativePositionTag The relative position tag. + */ + AreaStructureWrapper & SetLandmarkInfo(const Globals::LandmarkTag & landmarkTag, + const DataModel::Nullable & relativePositionTag) + { + areaDesc.landmarkInfo.SetNonNull(); + areaDesc.landmarkInfo.Value().landmarkTag = landmarkTag; + areaDesc.landmarkInfo.Value().positionTag = relativePositionTag; + + return *this; + } + + /** + * @brief Set the landmark information from a LandmarkInfoStruct object. + */ + AreaStructureWrapper & SetLandmarkInfo(DataModel::Nullable landmarkInfo) + { + if (landmarkInfo.IsNull()) { - auto sizeToCopy = std::min(sizeof(mLocationNameBuffer), aLocationName.size()); - memcpy(mLocationNameBuffer, aLocationName.data(), sizeToCopy); - areaDesc.locationInfo.Value().locationName = CharSpan(mLocationNameBuffer, sizeToCopy); + return SetLandmarkInfoNull(); } + + return SetLandmarkInfo(landmarkInfo.Value().landmarkTag, landmarkInfo.Value().positionTag); } /** - * @brief Compare the location's name with the given text. - * @param[in] aLocationName The name to compare. - * @return true if the location structure's name field matches aLocationName. - * False otherwise, including if the location structure's HomeLocation structure is null. + * @brief Compare the area's name with the given text. + * @param[in] aAreaName The name to compare. + * @return true if the area structure's name field matches aAreaName. + * False otherwise, including if the location structure's LocationInfo structure is null. */ - bool IsNameEqual(const CharSpan & aLocationName) const + bool IsNameEqual(const CharSpan & aAreaName) const { if (!areaDesc.locationInfo.IsNull()) { - return areaDesc.locationInfo.Value().locationName.data_equal(aLocationName); + return areaDesc.locationInfo.Value().locationName.data_equal(aAreaName); } return false; @@ -291,14 +265,14 @@ struct AreaStructureWrapper : public chip::app::Clusters::ServiceArea::Structs:: { if (areaDesc.locationInfo.IsNull()) { - return { mLocationNameBuffer, 0 }; + return { mAreaNameBuffer, 0 }; } return areaDesc.locationInfo.Value().locationName; } private: - char mLocationNameBuffer[kLocationNameMaxSize] = { 0 }; + char mAreaNameBuffer[kAreaNameMaxSize] = { 0 }; }; /** diff --git a/src/app/clusters/service-area-server/service-area-delegate.cpp b/src/app/clusters/service-area-server/service-area-delegate.cpp index 262fe047d22a1b..9b63eb5b9f2496 100644 --- a/src/app/clusters/service-area-server/service-area-delegate.cpp +++ b/src/app/clusters/service-area-server/service-area-delegate.cpp @@ -3,14 +3,14 @@ using namespace chip::app::Clusters::ServiceArea; -bool Delegate::GetSupportedLocationById(uint32_t aAreaId, uint32_t & listIndex, AreaStructureWrapper & aSupportedLocation) +bool Delegate::GetSupportedAreaById(uint32_t aAreaId, uint32_t & listIndex, AreaStructureWrapper & aSupportedArea) { listIndex = 0; - // simple linear iteration to find the location with the desired areaId. - while (GetSupportedLocationByIndex(listIndex, aSupportedLocation)) + // simple linear iteration to find the area with the desired areaId. + while (GetSupportedAreaByIndex(listIndex, aSupportedArea)) { - if (aSupportedLocation.areaID == aAreaId) + if (aSupportedArea.areaID == aAreaId) { return true; } @@ -45,14 +45,14 @@ bool Delegate::GetSupportedMapById(uint32_t aMapId, uint32_t & listIndex, MapStr return false; } -bool Delegate::IsSelectedLocation(uint32_t aAreaId) +bool Delegate::IsSelectedArea(uint32_t aAreaId) { uint32_t listIndex = 0; - uint32_t selectedLocation; + uint32_t selectedArea; - while (GetSelectedLocationByIndex(listIndex, selectedLocation)) + while (GetSelectedAreaByIndex(listIndex, selectedArea)) { - if (selectedLocation == aAreaId) + if (selectedArea == aAreaId) { return true; } diff --git a/src/app/clusters/service-area-server/service-area-delegate.h b/src/app/clusters/service-area-server/service-area-delegate.h index 823eaf6b537f19..e31abf98f9e745 100644 --- a/src/app/clusters/service-area-server/service-area-delegate.h +++ b/src/app/clusters/service-area-server/service-area-delegate.h @@ -115,8 +115,8 @@ class Delegate * been completed, using a vendor defined order. If the server has completed operating at all locations on the SelectedAreas * attribute list, the server SHALL stop operating. * - * @note If the Status field is set to InvalidLocationList, the StatusText field SHALL be an empty string. - * If the Status field is not set to Success, or InvalidLocationList, the StatusText field SHALL include a vendor defined + * @note If the Status field is set to InvalidAreaList, the StatusText field SHALL be an empty string. + * If the Status field is not set to Success, or InvalidAreaList, the StatusText field SHALL include a vendor defined * error description which can be used to explain the error to the user. For example, if the Status field is set to * InvalidInMode, the StatusText field SHOULD indicate why the request is not allowed, given the current mode of the device, * which may involve other clusters. @@ -124,7 +124,7 @@ class Delegate virtual bool HandleSkipCurrentArea(uint32_t skippedArea, MutableCharSpan skipStatusText) { // device support of this command is optional - CopyCharSpanToMutableCharSpan("Skip Current Location command not supported by device"_span, skipStatusText); + CopyCharSpanToMutableCharSpan("Skip Current Area command not supported by device"_span, skipStatusText); return false; } @@ -148,45 +148,45 @@ class Delegate virtual uint32_t GetNumberOfSupportedAreas() = 0; /** - * @brief Get a supported location using the position in the list. + * @brief Get a supported area using the position in the list. * @param[in] listIndex the position in the list. - * @param[out] aSupportedLocation copy of the location contents, if found. - * @return true if location found, false otherwise. + * @param[out] aSupportedArea a copy of the area contents, if found. + * @return true if an area is found, false otherwise. */ - virtual bool GetSupportedLocationByIndex(uint32_t listIndex, AreaStructureWrapper & aSupportedLocation) = 0; + virtual bool GetSupportedAreaByIndex(uint32_t listIndex, AreaStructureWrapper & aSupportedArea) = 0; /** - * @brief Get a supported location that matches a areaID. + * @brief Get a supported area that matches a areaID. * @param[in] aAreaId the areaID to search for. - * @param[out] listIndex the location's index in the list, if found. - * @param[out] aSupportedLocation copy of the location contents, if found. - * @return true if location found, false otherwise. + * @param[out] listIndex the area's index in the list, if found. + * @param[out] aSupportedArea a copy of the area contents, if found. + * @return true if an area is found, false otherwise. * * @note may be overloaded in device implementation for optimization, if desired. */ - virtual bool GetSupportedLocationById(uint32_t aAreaId, uint32_t & listIndex, AreaStructureWrapper & aSupportedLocation); + virtual bool GetSupportedAreaById(uint32_t aAreaId, uint32_t & listIndex, AreaStructureWrapper & aSupportedArea); /** - * This method is called by the server instance to add a new location to the list. - * The server instance will ensure that the newArea is a valid, unique location. - * @param [in] newArea new location to add. - * @param [out] listIndex filled with the list index for the new location, if successful. + * This method is called by the server instance to add a new area to the list. + * The server instance will ensure that the newArea is a valid, unique area. + * @param [in] newArea new area to add. + * @param [out] listIndex filled with the list index for the new area, if successful. * @return true if successful, false otherwise. - * @note this function SHOULD double check that the added location won't exceed the maximum list size. + * @note this method SHOULD double check that the added area won't exceed the maximum list size. */ - virtual bool AddSupportedLocation(const AreaStructureWrapper & newArea, uint32_t & listIndex) = 0; + virtual bool AddSupportedArea(const AreaStructureWrapper & newArea, uint32_t & listIndex) = 0; /** - * This method is called by the server instance to modify an existing location in the list. - * The server instance will ensure that the modifiedLocation is a valid, unique location. - * @param[in] listIndex The index of the location being modified. - * @param[in] modifiedLocation A location with the modified contents. + * This method is called by the server instance to modify an existing area in the list. + * The server instance will ensure that the modifiedArea is a valid, unique area. + * @param[in] listIndex The index of the area being modified. + * @param[in] modifiedArea An area with the modified contents. * @return true if successful, false otherwise. * * @note this function SHOULD double check that newArea's areaID matches the object at listIndex. */ - virtual bool ModifySupportedLocation(uint32_t listIndex, const AreaStructureWrapper & modifiedLocation) = 0; + virtual bool ModifySupportedArea(uint32_t listIndex, const AreaStructureWrapper & modifiedArea) = 0; /** * @return true if supported locations was not already null, false otherwise. @@ -276,36 +276,36 @@ class Delegate virtual bool ClearSupportedMaps() = 0; //************************************************************************* - // Selected Locations accessors + // Selected Areas accessors virtual uint32_t GetNumberOfSelectedAreas() = 0; /** - * @brief Get a selected location using the position in the list. + * @brief Get a selected area using the position in the list. * @param[in] listIndex the position in the list. - * @param[out] selectedLocation the selected location value, if found. - * @return true if a selected location is found, false otherwise. + * @param[out] selectedArea the selected area value, if found. + * @return true if a selected area is found, false otherwise. */ - virtual bool GetSelectedLocationByIndex(uint32_t listIndex, uint32_t & selectedLocation) = 0; + virtual bool GetSelectedAreaByIndex(uint32_t listIndex, uint32_t & selectedArea) = 0; /** * @return true if the aAreaId areaID is found in the SelectedAreas list, false otherwise. * * @note may be overloaded in device implementation for optimization, if desired. */ - virtual bool IsSelectedLocation(uint32_t aAreaId); + virtual bool IsSelectedArea(uint32_t aAreaId); /** - * This method is called by the server instance to add a new selected location to the list. - * The server instance will ensure that the aAreaId references a SUPPORTED location, and is unique within selected - * locations. + * This method is called by the server instance to add a new selected area to the list. + * The server instance will ensure that the aAreaId references a SUPPORTED area, and is unique within selected + * areas. * @param[in] aAreaId The new areaID to add. - * @param[out] listIndex filled with the list index of the new location, if successful. + * @param[out] listIndex filled with the list index of the new area, if successful. * @return true if successful, false otherwise. * - * @note this function SHOULD double check that the added location won't exceed the maximum list size. + * @note this function SHOULD double check that the added area won't exceed the maximum list size. */ - virtual bool AddSelectedLocation(uint32_t aAreaId, uint32_t & listIndex) = 0; + virtual bool AddSelectedArea(uint32_t aAreaId, uint32_t & listIndex) = 0; /** * @return true if selected locations was not already null, false otherwise. diff --git a/src/app/clusters/service-area-server/service-area-server.cpp b/src/app/clusters/service-area-server/service-area-server.cpp index cb5c3b144ba640..d745c04b5d468e 100644 --- a/src/app/clusters/service-area-server/service-area-server.cpp +++ b/src/app/clusters/service-area-server/service-area-server.cpp @@ -57,7 +57,7 @@ Instance::Instance(Delegate * aDelegate, EndpointId aEndpointId, BitMaskInit(); } @@ -139,11 +139,11 @@ CHIP_ERROR Instance::ReadSupportedAreas(AttributeValueEncoder & aEncoder) return aEncoder.EncodeList([this](const auto & encoder) -> CHIP_ERROR { uint8_t locationIndex = 0; - AreaStructureWrapper supportedLocation; + AreaStructureWrapper supportedArea; - while (mDelegate->GetSupportedLocationByIndex(locationIndex++, supportedLocation)) + while (mDelegate->GetSupportedAreaByIndex(locationIndex++, supportedArea)) { - ReturnErrorOnFailure(encoder.Encode(supportedLocation)); + ReturnErrorOnFailure(encoder.Encode(supportedArea)); } return CHIP_NO_ERROR; }); @@ -177,11 +177,11 @@ CHIP_ERROR Instance::ReadSelectedAreas(AttributeValueEncoder & aEncoder) return aEncoder.EncodeList([this](const auto & encoder) -> CHIP_ERROR { uint32_t locationIndex = 0; - uint32_t selectedLocation; + uint32_t selectedArea; - while (mDelegate->GetSelectedLocationByIndex(locationIndex++, selectedLocation)) + while (mDelegate->GetSelectedAreaByIndex(locationIndex++, selectedArea)) { - ReturnErrorOnFailure(encoder.Encode(selectedLocation)); + ReturnErrorOnFailure(encoder.Encode(selectedArea)); } return CHIP_NO_ERROR; }); @@ -222,10 +222,10 @@ void Instance::HandleSelectAreasCmd(HandlerContext & ctx, const Commands::Select ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); }; - size_t numberOfLocations = 0; - // Get the number of Selected Locations in the command parameter and check that it is valid. + size_t numberOfAreas = 0; + // Get the number of Selected Areas in the command parameter and check that it is valid. { - if (CHIP_NO_ERROR != req.newAreas.ComputeSize(&numberOfLocations)) + if (CHIP_NO_ERROR != req.newAreas.ComputeSize(&numberOfAreas)) { ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); return; @@ -233,7 +233,7 @@ void Instance::HandleSelectAreasCmd(HandlerContext & ctx, const Commands::Select // If the device determines that it can't operate at all locations from the list, // the SelectAreasResponse command's Status field SHALL indicate InvalidSet. - if (numberOfLocations > kMaxNumSelectedAreas) + if (numberOfAreas > kMaxNumSelectedAreas) { exitResponse(SelectAreasStatus::kInvalidSet, "invalid number of locations"_span); return; @@ -241,36 +241,35 @@ void Instance::HandleSelectAreasCmd(HandlerContext & ctx, const Commands::Select } // if number of selected locations in parameter matches number in attribute - the locations *might* be the same - bool matchesCurrentSelectedAreas = (numberOfLocations == mDelegate->GetNumberOfSelectedAreas()); + bool matchesCurrentSelectedAreas = (numberOfAreas == mDelegate->GetNumberOfSelectedAreas()); - if (numberOfLocations != 0) + if (numberOfAreas != 0) { // do as much parameter validation as we can { uint32_t ignoredIndex = 0; - uint32_t oldSelectedLocation; - uint32_t i = 0; - auto iLocationIter = req.newAreas.begin(); - while (iLocationIter.Next()) + uint32_t oldSelectedArea; + uint32_t i = 0; + auto iAreaIter = req.newAreas.begin(); + while (iAreaIter.Next()) { - uint32_t aSelectedLocation = iLocationIter.GetValue(); + uint32_t aSelectedArea = iAreaIter.GetValue(); // each item in this list SHALL match the AreaID field of an entry on the SupportedAreas attribute's list - // If the Status field is set to UnsupportedLocation, the StatusText field SHALL be an empty string. - if (!IsSupportedLocation(aSelectedLocation)) + // If the Status field is set to UnsupportedArea, the StatusText field SHALL be an empty string. + if (!IsSupportedArea(aSelectedArea)) { exitResponse(SelectAreasStatus::kUnsupportedArea, ""_span); return; } // Checking for duplicate locations. - uint32_t j = 0; - auto jLocationIter = req.newAreas.begin(); + uint32_t j = 0; + auto jAreaIter = req.newAreas.begin(); while (j < i) { - jLocationIter - .Next(); // Since j < i and i is valid, we can safely call Next() without checking the return value. - if (jLocationIter.GetValue() == aSelectedLocation) + jAreaIter.Next(); // Since j < i and i is valid, we can safely call Next() without checking the return value. + if (jAreaIter.GetValue() == aSelectedArea) { exitResponse(SelectAreasStatus::kDuplicatedAreas, ""_span); return; @@ -281,8 +280,7 @@ void Instance::HandleSelectAreasCmd(HandlerContext & ctx, const Commands::Select // check to see if parameter list and attribute still match if (matchesCurrentSelectedAreas) { - if (!mDelegate->GetSelectedLocationByIndex(ignoredIndex, oldSelectedLocation) || - (aSelectedLocation != oldSelectedLocation)) + if (!mDelegate->GetSelectedAreaByIndex(ignoredIndex, oldSelectedArea) || (aSelectedArea != oldSelectedArea)) { matchesCurrentSelectedAreas = false; } @@ -292,7 +290,7 @@ void Instance::HandleSelectAreasCmd(HandlerContext & ctx, const Commands::Select } // after iterating with Next through DecodableType - check for failure - if (CHIP_NO_ERROR != iLocationIter.GetStatus()) + if (CHIP_NO_ERROR != iAreaIter.GetStatus()) { ctx.mCommandHandler.AddStatus(ctx.mRequestPath, Status::InvalidCommand); return; @@ -342,13 +340,13 @@ void Instance::HandleSelectAreasCmd(HandlerContext & ctx, const Commands::Select // and the SelectedAreas attribute SHALL be set to the value of the newAreas field. mDelegate->ClearSelectedAreas(); - if (numberOfLocations != 0) + if (numberOfAreas != 0) { auto locationIter = req.newAreas.begin(); uint32_t ignored; while (locationIter.Next()) { - mDelegate->AddSelectedLocation(locationIter.GetValue(), ignored); + mDelegate->AddSelectedArea(locationIter.GetValue(), ignored); } } @@ -371,26 +369,26 @@ void Instance::HandleSkipCurrentAreaCmd(HandlerContext & ctx, const Commands::Sk ctx.mCommandHandler.AddResponse(ctx.mRequestPath, response); }; - // If the SelectedAreas attribute is null, the response status should be set to InvalidLocationList. - // If the Status field is set to InvalidLocationList, the StatusText field SHALL be an empty string. + // If the SelectedAreas attribute is null, the response status should be set to InvalidAreaList. + // If the Status field is set to InvalidAreaList, the StatusText field SHALL be an empty string. if (mDelegate->GetNumberOfSelectedAreas() == 0) { - ChipLogError(Zcl, "Selected Locations attribute is null"); + ChipLogError(Zcl, "Selected Areas attribute is null"); exitResponse(SkipAreaStatus::kInvalidAreaList, ""_span); return; } // If the CurrentArea attribute is null, the status should be set to InvalidInMode. - // If the Status field is not set to Success, or InvalidLocationList, the StatusText field SHALL include a vendor defined error + // If the Status field is not set to Success, or InvalidAreaList, the StatusText field SHALL include a vendor defined error // description. if (mCurrentArea.IsNull()) { - exitResponse(SkipAreaStatus::kInvalidInMode, "Current Location attribute is null"_span); + exitResponse(SkipAreaStatus::kInvalidInMode, "Current Area attribute is null"_span); return; } // have the device attempt to skip - // If the Status field is not set to Success, or InvalidLocationList, the StatusText field SHALL include a vendor defined error + // If the Status field is not set to Success, or InvalidAreaList, the StatusText field SHALL include a vendor defined error // description. InvalidInMode | The received request cannot be handled due to the current mode of the device. (skipStatusText to // be filled out by delegated function on failure.) char skipStatusBuffer[kMaxSizeStatusText]; @@ -437,37 +435,35 @@ void Instance::NotifyProgressChanged() } // **************************************************************************** -// Supported Locations manipulators +// Supported Areas manipulators -bool Instance::IsSupportedLocation(uint32_t aAreaId) +bool Instance::IsSupportedArea(uint32_t aAreaId) { uint32_t ignoredIndex; - AreaStructureWrapper ignoredLocation; + AreaStructureWrapper ignoredArea; - return mDelegate->GetSupportedLocationById(aAreaId, ignoredIndex, ignoredLocation); + return mDelegate->GetSupportedAreaById(aAreaId, ignoredIndex, ignoredArea); } -bool Instance::IsValidSupportedLocation(const AreaStructureWrapper & aLocation) +bool Instance::IsValidSupportedArea(const AreaStructureWrapper & aArea) { // If the LocationInfo field is null, the LandmarkInfo field SHALL NOT be null. // If the LandmarkInfo field is null, the LocationInfo field SHALL NOT be null. - if (aLocation.areaDesc.locationInfo.IsNull() && aLocation.areaDesc.landmarkInfo.IsNull()) + if (aArea.areaDesc.locationInfo.IsNull() && aArea.areaDesc.landmarkInfo.IsNull()) { - ChipLogDetail(Zcl, "IsValidAsSupportedLocation %u - must have locationInfo and/or LandmarkInfo", aLocation.areaID); + ChipLogDetail(Zcl, "IsValidAsSupportedArea %u - must have locationInfo and/or LandmarkInfo", aArea.areaID); return false; } // If LocationInfo is not null, and its LocationName field is an empty string, at least one of the following SHALL NOT // be null: LocationInfo's FloorNumber field, LocationInfo's AreaType field, the LandmarkInfo - if (!aLocation.areaDesc.locationInfo.IsNull()) + if (!aArea.areaDesc.locationInfo.IsNull()) { - if (aLocation.areaDesc.locationInfo.Value().locationName.empty() && - aLocation.areaDesc.locationInfo.Value().floorNumber.IsNull() && - aLocation.areaDesc.locationInfo.Value().areaType.IsNull() && aLocation.areaDesc.landmarkInfo.IsNull()) + if (aArea.areaDesc.locationInfo.Value().locationName.empty() && aArea.areaDesc.locationInfo.Value().floorNumber.IsNull() && + aArea.areaDesc.locationInfo.Value().areaType.IsNull() && aArea.areaDesc.landmarkInfo.IsNull()) { - ChipLogDetail( - Zcl, "IsValidAsSupportedLocation %u - LocationName is empty string, FloorNumber, AreaType, LandmarkInfo are null", - aLocation.areaID); + ChipLogDetail(Zcl, "IsValidAsSupportedArea %u - AreaName is empty string, FloorNumber, AreaType, LandmarkInfo are null", + aArea.areaID); return false; } } @@ -488,20 +484,20 @@ bool Instance::IsValidSupportedLocation(const AreaStructureWrapper & aLocation) if (shouldMapsBeNull) { - if (!aLocation.mapID.IsNull()) + if (!aArea.mapID.IsNull()) { - ChipLogDetail(Zcl, "IsValidSupportedLocation %u - map Id %u is not in empty supported map list", aLocation.areaID, - aLocation.mapID.Value()); + ChipLogDetail(Zcl, "IsValidSupportedArea %u - map Id %u is not in empty supported map list", aArea.areaID, + aArea.mapID.Value()); return false; } } else { // If the SupportedMaps attribute is not null, mapID SHALL be the ID of an entry from the SupportedMaps attribute. - if (!IsSupportedMap(aLocation.mapID.Value())) + if (!IsSupportedMap(aArea.mapID.Value())) { - ChipLogError(Zcl, "IsValidSupportedLocation %u - map Id %u is not in supported map list", aLocation.areaID, - aLocation.mapID.Value()); + ChipLogError(Zcl, "IsValidSupportedArea %u - map Id %u is not in supported map list", aArea.areaID, + aArea.mapID.Value()); return false; } } @@ -509,7 +505,7 @@ bool Instance::IsValidSupportedLocation(const AreaStructureWrapper & aLocation) return true; } -bool Instance::IsUniqueSupportedLocation(const AreaStructureWrapper & aLocation, bool ignoreAreaId) +bool Instance::IsUniqueSupportedArea(const AreaStructureWrapper & aArea, bool ignoreAreaId) { BitMask config; @@ -528,9 +524,9 @@ bool Instance::IsUniqueSupportedLocation(const AreaStructureWrapper & aLocation, uint8_t locationIndex = 0; AreaStructureWrapper entry; - while (mDelegate->GetSupportedLocationByIndex(locationIndex++, entry)) + while (mDelegate->GetSupportedAreaByIndex(locationIndex++, entry)) { - if (aLocation.IsEqual(entry, config)) + if (aArea.IsEqual(entry, config)) { return false; } @@ -576,15 +572,8 @@ bool Instance::ReportEstimatedEndTimeChange(const DataModel::Nullable return (aEstimatedEndTime.Value() < mEstimatedEndTime.Value()); } -bool Instance::AddSupportedLocation(uint32_t aAreaId, const DataModel::Nullable & aMapId, const CharSpan & aLocationName, - const DataModel::Nullable & aFloorNumber, - const DataModel::Nullable & aAreaType, - const DataModel::Nullable & aLandmarkTag, - const DataModel::Nullable & aRelativePositionTag) +bool Instance::AddSupportedArea(AreaStructureWrapper & aNewArea) { - // Create location object for validation. - AreaStructureWrapper aNewArea(aAreaId, aMapId, aLocationName, aFloorNumber, aAreaType, aLandmarkTag, aRelativePositionTag); - // Does device mode allow this attribute to be updated? if (!mDelegate->IsSupportedAreasChangeAllowed()) { @@ -594,30 +583,30 @@ bool Instance::AddSupportedLocation(uint32_t aAreaId, const DataModel::Nullable< // Check there is space for the entry. if (mDelegate->GetNumberOfSupportedAreas() >= kMaxNumSupportedAreas) { - ChipLogError(Zcl, "AddSupportedLocation %u - too many entries", aAreaId); + ChipLogError(Zcl, "AddSupportedArea %u - too many entries", aNewArea.areaID); return false; } // Verify cluster requirements concerning valid fields and field relationships. - if (!IsValidSupportedLocation(aNewArea)) + if (!IsValidSupportedArea(aNewArea)) { - ChipLogError(Zcl, "AddSupportedLocation %u - not a valid location object", aNewArea.areaID); + ChipLogError(Zcl, "AddSupportedArea %u - not a valid location object", aNewArea.areaID); return false; } - // Each entry in Supported Locations SHALL have a unique value for the ID field. + // Each entry in Supported Areas SHALL have a unique value for the ID field. // If the SupportedMaps attribute is not null, each entry in this list SHALL have a unique value for the combination of the - // MapID and LocationInfo fields. If the SupportedMaps attribute is null, each entry in this list SHALL have a unique value for - // the LocationInfo field. - if (!IsUniqueSupportedLocation(aNewArea, false)) + // MapID and AreaInfo fields. If the SupportedMaps attribute is null, each entry in this list SHALL have a unique value for + // the AreaInfo field. + if (!IsUniqueSupportedArea(aNewArea, false)) { - ChipLogError(Zcl, "AddSupportedLocation %u - not a unique location object", aNewArea.areaID); + ChipLogError(Zcl, "AddSupportedArea %u - not a unique location object", aNewArea.areaID); return false; } - // Add the SupportedLocation to the SupportedAreas attribute. + // Add the SupportedArea to the SupportedAreas attribute. uint32_t ignoredIndex; - if (!mDelegate->AddSupportedLocation(aNewArea, ignoredIndex)) + if (!mDelegate->AddSupportedArea(aNewArea, ignoredIndex)) { return false; } @@ -626,27 +615,23 @@ bool Instance::AddSupportedLocation(uint32_t aAreaId, const DataModel::Nullable< return true; } -bool Instance::ModifySupportedLocation(uint32_t aAreaId, const DataModel::Nullable & aMapId, - const CharSpan & aLocationName, const DataModel::Nullable & aFloorNumber, - const DataModel::Nullable & aAreaType, - const DataModel::Nullable & aLandmarkTag, - const DataModel::Nullable & aRelativePositionTag) +bool Instance::ModifySupportedArea(AreaStructureWrapper & aNewArea) { bool mapIDChanged = false; uint32_t listIndex; // get existing supported location to modify - AreaStructureWrapper supportedLocation; - if (!mDelegate->GetSupportedLocationById(aAreaId, listIndex, supportedLocation)) + AreaStructureWrapper supportedArea; + if (!mDelegate->GetSupportedAreaById(aNewArea.areaID, listIndex, supportedArea)) { - ChipLogError(Zcl, "ModifySupportedLocation %u - not a supported areaID", aAreaId); + ChipLogError(Zcl, "ModifySupportedArea %u - not a supported areaID", aNewArea.areaID); return false; } { // check for mapID change - if ((aMapId.IsNull() != supportedLocation.mapID.IsNull()) || - (!aMapId.IsNull() && !supportedLocation.mapID.IsNull() && (aMapId.Value() != supportedLocation.mapID.Value()))) + if ((aNewArea.mapID.IsNull() != supportedArea.mapID.IsNull()) || + (!aNewArea.mapID.IsNull() && !supportedArea.mapID.IsNull() && (aNewArea.mapID.Value() != supportedArea.mapID.Value()))) { // does device mode allow this attribute to be updated? if (!mDelegate->IsSupportedAreasChangeAllowed()) @@ -656,26 +641,23 @@ bool Instance::ModifySupportedLocation(uint32_t aAreaId, const DataModel::Nullab mapIDChanged = true; } - // create new location object for validation - AreaStructureWrapper aNewArea(aAreaId, aMapId, aLocationName, aFloorNumber, aAreaType, aLandmarkTag, aRelativePositionTag); - // verify cluster requirements concerning valid fields and field relationships - if (!IsValidSupportedLocation(aNewArea)) + if (!IsValidSupportedArea(aNewArea)) { - ChipLogError(Zcl, "ModifySupportedLocation %u - not a valid location object", aNewArea.areaID); + ChipLogError(Zcl, "ModifySupportedArea %u - not a valid location object", aNewArea.areaID); return false; } // Updated location description must not match another existing location description. // We ignore comparing the area ID as one of the locations will match this one. - if (!IsUniqueSupportedLocation(aNewArea, true)) + if (!IsUniqueSupportedArea(aNewArea, true)) { - ChipLogError(Zcl, "ModifySupportedLocation %u - not a unique location object", aNewArea.areaID); + ChipLogError(Zcl, "ModifySupportedArea %u - not a unique location object", aNewArea.areaID); return false; } // Replace the supported location with the modified location. - if (!mDelegate->ModifySupportedLocation(listIndex, aNewArea)) + if (!mDelegate->ModifySupportedArea(listIndex, aNewArea)) { return false; } @@ -844,28 +826,28 @@ bool Instance::ClearSupportedMaps() } //************************************************************************* -// Selected Locations manipulators +// Selected Areas manipulators -bool Instance::AddSelectedLocation(uint32_t & aSelectedLocation) +bool Instance::AddSelectedArea(uint32_t & aSelectedArea) { // check max# of list entries if (mDelegate->GetNumberOfSelectedAreas() >= kMaxNumSelectedAreas) { - ChipLogError(Zcl, "AddSelectedLocation %u - maximum number of entries", aSelectedLocation); + ChipLogError(Zcl, "AddSelectedArea %u - maximum number of entries", aSelectedArea); return false; } // each item in this list SHALL match the AreaID field of an entry on the SupportedAreas attribute's list - if (!IsSupportedLocation(aSelectedLocation)) + if (!IsSupportedArea(aSelectedArea)) { - ChipLogError(Zcl, "AddSelectedLocation %u - not a supported location", aSelectedLocation); + ChipLogError(Zcl, "AddSelectedArea %u - not a supported location", aSelectedArea); return false; } // each entry in this list SHALL have a unique value - if (mDelegate->IsSelectedLocation(aSelectedLocation)) + if (mDelegate->IsSelectedArea(aSelectedArea)) { - ChipLogError(Zcl, "AddSelectedLocation %u - duplicated location", aSelectedLocation); + ChipLogError(Zcl, "AddSelectedArea %u - duplicated location", aSelectedArea); return false; } @@ -875,13 +857,13 @@ bool Instance::AddSelectedLocation(uint32_t & aSelectedLocation) if (!mDelegate->IsSetSelectedAreasAllowed(locationStatusText)) { - ChipLogError(Zcl, "AddSelectedLocation %u - %.*s", aSelectedLocation, static_cast(locationStatusText.size()), + ChipLogError(Zcl, "AddSelectedArea %u - %.*s", aSelectedArea, static_cast(locationStatusText.size()), locationStatusText.data()); return false; } uint32_t ignoredIndex; - return mDelegate->AddSelectedLocation(aSelectedLocation, ignoredIndex); + return mDelegate->AddSelectedArea(aSelectedArea, ignoredIndex); } bool Instance::ClearSelectedAreas() @@ -896,7 +878,7 @@ bool Instance::ClearSelectedAreas() } //************************************************************************* -// Current Location manipulators +// Current Area manipulators DataModel::Nullable Instance::GetCurrentArea() { @@ -907,7 +889,7 @@ bool Instance::SetCurrentArea(const DataModel::Nullable & aCurrentArea { // If not null, the value of this attribute SHALL match the AreaID field of an entry on the SupportedAreas attribute's // list. - if ((!aCurrentArea.IsNull()) && (!IsSupportedLocation(aCurrentArea.Value()))) + if ((!aCurrentArea.IsNull()) && (!IsSupportedArea(aCurrentArea.Value()))) { ChipLogError(Zcl, "SetCurrentArea %u - location is not supported", aCurrentArea.Value()); return false; @@ -943,7 +925,7 @@ bool Instance::SetEstimatedEndTime(const DataModel::Nullable & aEstima // EstimatedEndTime SHALL be null if the CurrentArea attribute is null. if (mCurrentArea.IsNull() && !aEstimatedEndTime.IsNull()) { - ChipLogError(Zcl, "SetEstimatedEndTime - must be null if Current Location is null"); + ChipLogError(Zcl, "SetEstimatedEndTime - must be null if Current Area is null"); return false; } @@ -976,7 +958,7 @@ bool Instance::AddPendingProgressElement(uint32_t aAreaId) } // For each entry in this list, the AreaID field SHALL match an entry on the SupportedAreas attribute's list. - if (!IsSupportedLocation(aAreaId)) + if (!IsSupportedArea(aAreaId)) { ChipLogError(Zcl, "AddPendingProgressElement - not a supported location %u", aAreaId); return false; diff --git a/src/app/clusters/service-area-server/service-area-server.h b/src/app/clusters/service-area-server/service-area-server.h index 815ff2ed0eac72..811499f3ff7fb2 100644 --- a/src/app/clusters/service-area-server/service-area-server.h +++ b/src/app/clusters/service-area-server/service-area-server.h @@ -137,28 +137,28 @@ class Instance : public AttributeAccessInterface, public CommandHandlerInterface void NotifyProgressChanged(); //************************************************************************* - // Supported Locations manipulators + // Supported Areas manipulators /** - * @return true if a location with the aAreaId ID exists in the supported locations attribute. False otherwise. + * @return true if an area with the aAreaId ID exists in the supported areas attribute. False otherwise. */ - bool IsSupportedLocation(uint32_t aAreaId); + bool IsSupportedArea(uint32_t aAreaId); /** - * @brief Check if the given location adheres to the restrictions required by the supported locations attribute. - * @return true if the aLocation meets all checks. + * @brief Check if the given area adheres to the restrictions required by the supported areas attribute. + * @return true if the aArea meets all checks. */ - bool IsValidSupportedLocation(const AreaStructureWrapper & aLocation); + bool IsValidSupportedArea(const AreaStructureWrapper & aArea); /** - * @brief check if aLocation is unique with regard to supported locations. - * @param[in] aLocation the location to check. + * @brief check if aArea is unique with regard to supported areas. + * @param[in] aArea the area to check. * @param[out] ignoreAreaId if true, we do not check if the area ID is unique. - * @return true if there isn't a location in supported locations that matches aLocation. + * @return true if there isn't an area in supported areas that matches aArea. * * @note This method may ignore checking the MapId uniqueness. This depends on whether the SupportedMaps attribute is null. */ - bool IsUniqueSupportedLocation(const AreaStructureWrapper & aLocation, bool ignoreAreaId); + bool IsUniqueSupportedArea(const AreaStructureWrapper & aArea, bool ignoreAreaId); /** * @brief Check if changing the estimated end time attribute to aEstimatedEndTime requires the change to be reported. @@ -170,47 +170,23 @@ class Instance : public AttributeAccessInterface, public CommandHandlerInterface public: /** * @brief Add new location to the supported locations list. - * @param[in] aAreaId unique identifier of this location. - * @param[in] aMapId identifier of supported map. - * @param[in] aLocationName human readable name for this location (empty string if not used). - * @param[in] aFloorNumber represents floor level - negative values for below ground. - * @param[in] aAreaType common namespace Area tag - indicates an association of the location with an indoor or outdoor area of a - * home. - * @param[in] aLandmarkTag common namespace Landmark tag - indicates an association of the location with a home landmark. - * @param[in] aRelativePositionTag common namespace Relative Position tag - indicates the position of the location with respect - * to the landmark. + * @param[in] aNewArea The area to add. * @return true if the new location passed validation checks and was successfully added to the list. * - * @note if aLocationName is larger than kLocationNameMaxSize, it will be truncated. + * @note if aNewArea is larger than kAreaNameMaxSize, it will be truncated. */ - bool AddSupportedLocation(uint32_t aAreaId, const DataModel::Nullable & aMapId, const CharSpan & aLocationName, - const DataModel::Nullable & aFloorNumber, - const DataModel::Nullable & aAreaType, - const DataModel::Nullable & aLandmarkTag, - const DataModel::Nullable & aRelativePositionTag); + bool AddSupportedArea(AreaStructureWrapper & aNewArea); /** * @brief Modify/replace an existing location in the supported locations list. - * @param[in] aAreaId unique identifier of this location. - * @param[in] aMapId identifier of supported map (will not be modified). - * @param[in] aLocationName human readable name for this location (empty string if not used). - * @param[in] aFloorNumber represents floor level - negative values for below ground. - * @param[in] aAreaType common namespace Area tag - indicates an association of the location with an indoor or outdoor area of a - * home. - * @param[in] aLandmarkTag common namespace Landmark tag - indicates an association of the location with a home landmark. - * @param[in] aRelativePositionTag common namespace Relative Position tag - indicates the position of the location with respect - * to the landmark. + * @param[in] aNewArea The area to add. * @return true if the location is a member of supported locations, the modifications pass all validation checks and the * location was modified. * - * @note if aLocationName is larger than kLocationNameMaxSize, it will be truncated. + * @note if aNewArea is larger than kAreaNameMaxSize, it will be truncated. * @note if mapID is changed, the delegate's HandleSupportedAreasUpdated method is called. */ - bool ModifySupportedLocation(uint32_t aAreaId, const DataModel::Nullable & aMapId, const CharSpan & aLocationName, - const DataModel::Nullable & aFloorNumber, - const DataModel::Nullable & aAreaType, - const DataModel::Nullable & aLandmarkTag, - const DataModel::Nullable & aRelativePositionTag); + bool ModifySupportedArea(AreaStructureWrapper & aNewArea); /** * @return true if the SupportedAreas attribute was not already null. @@ -253,14 +229,14 @@ class Instance : public AttributeAccessInterface, public CommandHandlerInterface bool ClearSupportedMaps(); //************************************************************************* - // Selected Locations manipulators + // Selected Areas manipulators /** - * @brief Add a selected location. - * @param[in] aSelectedLocation The areaID to add. + * @brief Add a selected area. + * @param[in] aSelectedArea The areaID to add. * @bool true if successfully added. */ - bool AddSelectedLocation(uint32_t & aSelectedLocation); + bool AddSelectedArea(uint32_t & aSelectedArea); /** * @return true if the SelectedAreas attribute was not already null. @@ -268,7 +244,7 @@ class Instance : public AttributeAccessInterface, public CommandHandlerInterface bool ClearSelectedAreas(); //************************************************************************* - // Current Location manipulators + // Current Area manipulators DataModel::Nullable GetCurrentArea(); diff --git a/src/app/clusters/thermostat-server/thermostat-delegate.h b/src/app/clusters/thermostat-server/thermostat-delegate.h index 0c09b9dd4d70fc..c8c21d898af167 100644 --- a/src/app/clusters/thermostat-server/thermostat-delegate.h +++ b/src/app/clusters/thermostat-server/thermostat-delegate.h @@ -38,6 +38,17 @@ class Delegate virtual ~Delegate() = default; + /** + * @brief Get the maximum timeout for atomically writing to a set of attributes + * + * @param[in] attributeRequests The list of attributes to write to. + * @param[out] timeoutRequest The timeout proposed by the client. + * @return The maximum allowed timeout; zero if the request is invalid. + */ + virtual std::optional + GetAtomicWriteTimeout(DataModel::DecodableList attributeRequests, + System::Clock::Milliseconds16 timeoutRequest) = 0; + /** * @brief Get the preset type at a given index in the PresetTypes attribute * diff --git a/src/app/clusters/thermostat-server/thermostat-server.cpp b/src/app/clusters/thermostat-server/thermostat-server.cpp index 6b8a50a6ffe7e0..91c045c5ff540e 100644 --- a/src/app/clusters/thermostat-server/thermostat-server.cpp +++ b/src/app/clusters/thermostat-server/thermostat-server.cpp @@ -27,6 +27,8 @@ #include #include #include +#include +#include #include #include @@ -116,8 +118,7 @@ void TimerExpiredCallback(System::Layer * systemLayer, void * callbackContext) VerifyOrReturn(delegate != nullptr, ChipLogError(Zcl, "Delegate is null. Unable to handle timer expired")); delegate->ClearPendingPresetList(); - gThermostatAttrAccess.SetAtomicWrite(endpoint, false); - gThermostatAttrAccess.SetAtomicWriteScopedNodeId(endpoint, ScopedNodeId()); + gThermostatAttrAccess.SetAtomicWrite(endpoint, ScopedNodeId(), kAtomicWriteState_Closed); } /** @@ -205,8 +206,7 @@ void resetAtomicWrite(Delegate * delegate, EndpointId endpoint) delegate->ClearPendingPresetList(); } ClearTimer(endpoint); - gThermostatAttrAccess.SetAtomicWrite(endpoint, false); - gThermostatAttrAccess.SetAtomicWriteScopedNodeId(endpoint, ScopedNodeId()); + gThermostatAttrAccess.SetAtomicWrite(endpoint, ScopedNodeId(), kAtomicWriteState_Closed); } /** @@ -605,14 +605,16 @@ void SetDefaultDelegate(EndpointId endpoint, Delegate * delegate) } } -void ThermostatAttrAccess::SetAtomicWrite(EndpointId endpoint, bool inProgress) +void ThermostatAttrAccess::SetAtomicWrite(EndpointId endpoint, ScopedNodeId originatorNodeId, AtomicWriteState state) { uint16_t ep = emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); - if (ep < ArraySize(mAtomicWriteState)) + if (ep < ArraySize(mAtomicWriteSessions)) { - mAtomicWriteState[ep] = inProgress; + mAtomicWriteSessions[ep].state = state; + mAtomicWriteSessions[ep].endpointId = endpoint; + mAtomicWriteSessions[ep].nodeId = originatorNodeId; } } @@ -622,9 +624,9 @@ bool ThermostatAttrAccess::InAtomicWrite(EndpointId endpoint) uint16_t ep = emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); - if (ep < ArraySize(mAtomicWriteState)) + if (ep < ArraySize(mAtomicWriteSessions)) { - inAtomicWrite = mAtomicWriteState[ep]; + inAtomicWrite = (mAtomicWriteSessions[ep].state == kAtomicWriteState_Open); } return inAtomicWrite; } @@ -649,26 +651,15 @@ bool ThermostatAttrAccess::InAtomicWrite(CommandHandler * commandObj, EndpointId return GetAtomicWriteScopedNodeId(endpoint) == sourceNodeId; } -void ThermostatAttrAccess::SetAtomicWriteScopedNodeId(EndpointId endpoint, ScopedNodeId originatorNodeId) -{ - uint16_t ep = - emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); - - if (ep < ArraySize(mAtomicWriteNodeIds)) - { - mAtomicWriteNodeIds[ep] = originatorNodeId; - } -} - ScopedNodeId ThermostatAttrAccess::GetAtomicWriteScopedNodeId(EndpointId endpoint) { ScopedNodeId originatorNodeId = ScopedNodeId(); uint16_t ep = emberAfGetClusterServerEndpointIndex(endpoint, Thermostat::Id, MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT); - if (ep < ArraySize(mAtomicWriteNodeIds)) + if (ep < ArraySize(mAtomicWriteSessions)) { - originatorNodeId = mAtomicWriteNodeIds[ep]; + originatorNodeId = mAtomicWriteSessions[ep].nodeId; } return originatorNodeId; } @@ -704,7 +695,7 @@ CHIP_ERROR ThermostatAttrAccess::Read(const ConcreteReadAttributePath & aPath, A } break; case PresetTypes::Id: { - Delegate * delegate = GetDelegate(aPath.mEndpointId); + auto delegate = GetDelegate(aPath.mEndpointId); VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is null")); return aEncoder.EncodeList([delegate](const auto & encoder) -> CHIP_ERROR { @@ -723,14 +714,14 @@ CHIP_ERROR ThermostatAttrAccess::Read(const ConcreteReadAttributePath & aPath, A } break; case NumberOfPresets::Id: { - Delegate * delegate = GetDelegate(aPath.mEndpointId); + auto delegate = GetDelegate(aPath.mEndpointId); VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is null")); ReturnErrorOnFailure(aEncoder.Encode(delegate->GetNumberOfPresets())); } break; case Presets::Id: { - Delegate * delegate = GetDelegate(aPath.mEndpointId); + auto delegate = GetDelegate(aPath.mEndpointId); VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is null")); auto & subjectDescriptor = aEncoder.GetSubjectDescriptor(); @@ -766,7 +757,7 @@ CHIP_ERROR ThermostatAttrAccess::Read(const ConcreteReadAttributePath & aPath, A } break; case ActivePresetHandle::Id: { - Delegate * delegate = GetDelegate(aPath.mEndpointId); + auto delegate = GetDelegate(aPath.mEndpointId); VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is null")); uint8_t buffer[kPresetHandleSize]; @@ -812,7 +803,7 @@ CHIP_ERROR ThermostatAttrAccess::Write(const ConcreteDataAttributePath & aPath, { case Presets::Id: { - Delegate * delegate = GetDelegate(endpoint); + auto delegate = GetDelegate(endpoint); VerifyOrReturnError(delegate != nullptr, CHIP_ERROR_INCORRECT_STATE, ChipLogError(Zcl, "Delegate is null")); // Presets are not editable, return INVALID_IN_STATE. @@ -897,7 +888,7 @@ CHIP_ERROR ThermostatAttrAccess::Write(const ConcreteDataAttributePath & aPath, return CHIP_NO_ERROR; } -CHIP_ERROR ThermostatAttrAccess::AppendPendingPreset(Delegate * delegate, const PresetStruct::Type & preset) +CHIP_ERROR ThermostatAttrAccess::AppendPendingPreset(Thermostat::Delegate * delegate, const PresetStruct::Type & preset) { if (!IsValidPresetEntry(preset)) { @@ -951,6 +942,23 @@ CHIP_ERROR ThermostatAttrAccess::AppendPendingPreset(Delegate * delegate, const return delegate->AppendToPendingPresetList(preset); } +void ThermostatAttrAccess::OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) +{ + for (size_t i = 0; i < ArraySize(mAtomicWriteSessions); ++i) + { + auto atomicWriteState = mAtomicWriteSessions[i]; + if (atomicWriteState.state == kAtomicWriteState_Open && atomicWriteState.nodeId.GetFabricIndex() == fabricIndex) + { + auto delegate = GetDelegate(atomicWriteState.endpointId); + if (delegate == nullptr) + { + continue; + } + resetAtomicWrite(delegate, atomicWriteState.endpointId); + } + } +} + } // namespace Thermostat } // namespace Clusters } // namespace app @@ -1394,8 +1402,6 @@ void handleAtomicBegin(CommandHandler * commandObj, const ConcreteCommandPath & return; } - auto timeout = commandData.timeout.Value(); - if (!validAtomicAttributes(commandData, false)) { commandObj->AddStatus(commandPath, imcode::InvalidCommand); @@ -1412,13 +1418,18 @@ void handleAtomicBegin(CommandHandler * commandObj, const ConcreteCommandPath & // needs to keep track of a pending preset list now. delegate->InitializePendingPresets(); - uint16_t maxTimeout = 5000; - timeout = std::min(timeout, maxTimeout); + auto timeout = + delegate->GetAtomicWriteTimeout(commandData.attributeRequests, System::Clock::Milliseconds16(commandData.timeout.Value())); - ScheduleTimer(endpoint, System::Clock::Milliseconds16(timeout)); - gThermostatAttrAccess.SetAtomicWrite(endpoint, true); - gThermostatAttrAccess.SetAtomicWriteScopedNodeId(endpoint, GetSourceScopedNodeId(commandObj)); - sendAtomicResponse(commandObj, commandPath, imcode::Success, imcode::Success, imcode::Success, MakeOptional(timeout)); + if (!timeout.has_value()) + { + commandObj->AddStatus(commandPath, imcode::InvalidCommand); + return; + } + ScheduleTimer(endpoint, timeout.value()); + gThermostatAttrAccess.SetAtomicWrite(endpoint, GetSourceScopedNodeId(commandObj), kAtomicWriteState_Open); + sendAtomicResponse(commandObj, commandPath, imcode::Success, imcode::Success, imcode::Success, + MakeOptional(timeout.value().count())); } imcode commitPresets(Delegate * delegate, EndpointId endpoint) @@ -1868,5 +1879,17 @@ bool emberAfThermostatClusterSetpointRaiseLowerCallback(app::CommandHandler * co void MatterThermostatPluginServerInitCallback() { + Server::GetInstance().GetFabricTable().AddFabricDelegate(&gThermostatAttrAccess); AttributeAccessInterfaceRegistry::Instance().Register(&gThermostatAttrAccess); } + +void MatterThermostatClusterServerShutdownCallback(EndpointId endpoint) +{ + ChipLogProgress(Zcl, "Shutting down thermostat server cluster on endpoint %d", endpoint); + Delegate * delegate = GetDelegate(endpoint); + + if (delegate != nullptr) + { + resetAtomicWrite(delegate, endpoint); + } +} diff --git a/src/app/clusters/thermostat-server/thermostat-server.h b/src/app/clusters/thermostat-server/thermostat-server.h index 306a2a625c57b6..ddede8a9bb13f9 100644 --- a/src/app/clusters/thermostat-server/thermostat-server.h +++ b/src/app/clusters/thermostat-server/thermostat-server.h @@ -37,10 +37,15 @@ namespace Thermostat { static constexpr size_t kThermostatEndpointCount = MATTER_DM_THERMOSTAT_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; +enum AtomicWriteState +{ + kAtomicWriteState_Closed = 0, + kAtomicWriteState_Open, +}; /** * @brief Thermostat Attribute Access Interface. */ -class ThermostatAttrAccess : public chip::app::AttributeAccessInterface +class ThermostatAttrAccess : public chip::app::AttributeAccessInterface, public chip::FabricTable::Delegate { public: ThermostatAttrAccess() : AttributeAccessInterface(Optional::Missing(), Thermostat::Id) {} @@ -48,15 +53,6 @@ class ThermostatAttrAccess : public chip::app::AttributeAccessInterface CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override; CHIP_ERROR Write(const ConcreteDataAttributePath & aPath, chip::app::AttributeValueDecoder & aDecoder) override; - /** - * @brief Sets the scoped node id of the originator that sent the last successful - * AtomicRequest of type BeginWrite for the given endpoint. - * - * @param[in] endpoint The endpoint. - * @param[in] originatorNodeId The originator scoped node id. - */ - void SetAtomicWriteScopedNodeId(EndpointId endpoint, ScopedNodeId originatorNodeId); - /** * @brief Gets the scoped node id of the originator that sent the last successful * AtomicRequest of type BeginWrite for the given endpoint. @@ -68,12 +64,13 @@ class ThermostatAttrAccess : public chip::app::AttributeAccessInterface ScopedNodeId GetAtomicWriteScopedNodeId(EndpointId endpoint); /** - * @brief Sets whether an atomic write is in progress for the given endpoint + * @brief Sets the atomic write state for the given endpoint and originatorNodeId * * @param[in] endpoint The endpoint. - * @param[in] inProgress Whether or not an atomic write is in progress. + * @param[in] originatorNodeId The originator scoped node id. + * @param[in] state Whether or not an atomic write is open or closed. */ - void SetAtomicWrite(EndpointId endpoint, bool inProgress); + void SetAtomicWrite(EndpointId endpoint, ScopedNodeId originatorNodeId, AtomicWriteState state); /** * @brief Gets whether an atomic write is in progress for the given endpoint @@ -105,10 +102,18 @@ class ThermostatAttrAccess : public chip::app::AttributeAccessInterface bool InAtomicWrite(CommandHandler * commandObj, EndpointId endpoint); private: - CHIP_ERROR AppendPendingPreset(Delegate * delegate, const Structs::PresetStruct::Type & preset); + CHIP_ERROR AppendPendingPreset(Thermostat::Delegate * delegate, const Structs::PresetStruct::Type & preset); + + void OnFabricRemoved(const FabricTable & fabricTable, FabricIndex fabricIndex) override; + + struct AtomicWriteSession + { + AtomicWriteState state = kAtomicWriteState_Closed; + ScopedNodeId nodeId; + EndpointId endpointId = kInvalidEndpointId; + }; - ScopedNodeId mAtomicWriteNodeIds[kThermostatEndpointCount]; - bool mAtomicWriteState[kThermostatEndpointCount]; + AtomicWriteSession mAtomicWriteSessions[kThermostatEndpointCount]; }; /** diff --git a/src/app/clusters/water-heater-management-server/water-heater-management-server.cpp b/src/app/clusters/water-heater-management-server/water-heater-management-server.cpp index 0806638dd7ee4d..3cbe76f60d38f4 100644 --- a/src/app/clusters/water-heater-management-server/water-heater-management-server.cpp +++ b/src/app/clusters/water-heater-management-server/water-heater-management-server.cpp @@ -117,12 +117,12 @@ void Instance::InvokeCommand(HandlerContext & handlerContext) void Instance::HandleBoost(HandlerContext & ctx, const Commands::Boost::DecodableType & commandData) { - uint32_t duration = commandData.duration; - Optional oneShot = commandData.oneShot; - Optional emergencyBoost = commandData.emergencyBoost; - Optional temporarySetpoint = commandData.temporarySetpoint; - Optional targetPercentage = commandData.targetPercentage; - Optional targetReheat = commandData.targetReheat; + uint32_t duration = commandData.boostInfo.duration; + Optional oneShot = commandData.boostInfo.oneShot; + Optional emergencyBoost = commandData.boostInfo.emergencyBoost; + Optional temporarySetpoint = commandData.boostInfo.temporarySetpoint; + Optional targetPercentage = commandData.boostInfo.targetPercentage; + Optional targetReheat = commandData.boostInfo.targetReheat; // Notify the appliance if the appliance hardware cannot be adjusted, then return Failure if (HasFeature(WaterHeaterManagement::Feature::kTankPercent)) diff --git a/src/app/clusters/water-heater-management-server/water-heater-management-server.h b/src/app/clusters/water-heater-management-server/water-heater-management-server.h index a0a48ab900c200..71a52de9b45eef 100644 --- a/src/app/clusters/water-heater-management-server/water-heater-management-server.h +++ b/src/app/clusters/water-heater-management-server/water-heater-management-server.h @@ -43,29 +43,52 @@ class Delegate void SetEndpointId(EndpointId aEndpoint) { mEndpointId = aEndpoint; } /** - * @brief Delegate should implement a handler to start boosting the water temperature as required. - * Upon receipt, the Water Heater SHALL transition into the BOOST state, which SHALL cause the water in the - * tank (or the TargetPercentage of the water, if included) to be heated towards the set point (or the - * TemporarySetpoint, if included), which in turn may cause a call for heat, even if the mode is OFF, or - * is TIMED and it is during one of the Off periods. + * @brief Delegate should implement a handler to start boosting the water + * temperature as required. Upon receipt, the Water Heater SHALL + * transition into the BOOST state, which SHALL cause the water in + * the tank (or the TargetPercentage of the water, if included) to be + * heated towards the set point (or the TemporarySetpoint, if + * included), which in turn may cause a call for heat, even if the + * mode is OFF, or is TIMED and it is during one of the Off periods. * - * @param duration Indicates the time period in seconds for which the BOOST state is activated before it automatically reverts - * to the previous mode (e.g. OFF, MANUAL or TIMED). - * @param oneShot Indicates whether the BOOST state should be automatically canceled once the hot water has first reached the - * set point temperature (or the TemporarySetpoint temperature, if specified) for the TargetPercentage (if - * specified). - * @param emergencyBoost Indicates that the consumer wants the water to be heated as quickly as practicable. This MAY cause - * multiple heat sources to be activated (e.g. a heat pump and direct electric heating element). - * @param temporarySetpoint Indicates the target temperature to which to heat the hot water for this Boost command. It SHALL be - * used instead of the normal set point temperature whilst the BOOST state is active. - * @param targetPercentage If the tank supports the TankPercent feature, this field indicates the amount of water that SHALL be - * heated by this Boost command before the heater is switched off. - * @param targetReheat If the tank supports the TankPercent feature, and the heating by this Boost command has ceased because - * the TargetPercentage of the water in the tank has been heated to the set point (or TemporarySetpoint if - * included), this field indicates the percentage to which the hot water in the tank SHALL be allowed to - * fall before again beginning to reheat it. + * @param duration Indicates the time period in seconds for which + * the BOOST state is activated before it + * automatically reverts to the previous mode + * (e.g. OFF, MANUAL or TIMED). * - * @return Success if the boost command is accepted; otherwise the command SHALL be rejected with appropriate error. + * @param oneShot Indicates whether the BOOST state should be + * automatically canceled once the hot water has + * first reached the set point temperature (or the + * TemporarySetpoint temperature, if specified) + * for the TargetPercentage (if specified). + * + * @param emergencyBoost Indicates that the consumer wants the water to + * be heated as quickly as practicable. This MAY + * cause multiple heat sources to be activated + * (e.g. a heat pump and direct electric heating + * element). + * + * @param temporarySetpoint Indicates the target temperature to which to + * heat the hot water for this Boost command. It + * SHALL be used instead of the normal set point + * temperature whilst the BOOST state is active. + * + * @param targetPercentage If the tank supports the TankPercent feature, + * this field indicates the amount of water that + * SHALL be heated by this Boost command before + * the heater is switched off. + * + * @param targetReheat If the tank supports the TankPercent feature, + * and the heating by this Boost command has + * ceased because the TargetPercentage of the + * water in the tank has been heated to the set + * point (or TemporarySetpoint if included), this + * field indicates the percentage to which the hot + * water in the tank SHALL be allowed to fall + * before again beginning to reheat it. + * + * @return Success if the boost command is accepted; otherwise the command + * SHALL be rejected with appropriate error. */ virtual Protocols::InteractionModel::Status HandleBoost(uint32_t duration, Optional oneShot, Optional emergencyBoost, Optional temporarySetpoint, @@ -73,8 +96,8 @@ class Delegate /** * @brief Delegate should implement a handler to cancel a boost command. - * Upon receipt, the Water Heater SHALL transition back from the BOOST state to the previous mode (e.g. OFF, - * MANUAL or TIMED). + * Upon receipt, the Water Heater SHALL transition back from the + * BOOST state to the previous mode (e.g. OFF, MANUAL or TIMED). * * @return It should report SUCCESS if successful and FAILURE otherwise. */ @@ -82,12 +105,12 @@ class Delegate // ------------------------------------------------------------------ // Get attribute methods - virtual BitMask GetHeaterTypes() = 0; - virtual BitMask GetHeatDemand() = 0; - virtual uint16_t GetTankVolume() = 0; - virtual int64_t GetEstimatedHeatRequired() = 0; - virtual Percent GetTankPercentage() = 0; - virtual BoostStateEnum GetBoostState() = 0; + virtual BitMask GetHeaterTypes() = 0; + virtual BitMask GetHeatDemand() = 0; + virtual uint16_t GetTankVolume() = 0; + virtual int64_t GetEstimatedHeatRequired() = 0; + virtual Percent GetTankPercentage() = 0; + virtual BoostStateEnum GetBoostState() = 0; protected: EndpointId mEndpointId = 0; diff --git a/src/app/common/templates/config-data.yaml b/src/app/common/templates/config-data.yaml index d8d276f53a0700..660ca0b3aa1df4 100644 --- a/src/app/common/templates/config-data.yaml +++ b/src/app/common/templates/config-data.yaml @@ -81,6 +81,7 @@ ClustersWithShutdownFunctions: - Color Control - Sample MEI - Scenes Management + - Thermostat ClustersWithPreAttributeChangeFunctions: - Door Lock diff --git a/src/app/tests/suites/certification/PICS.yaml b/src/app/tests/suites/certification/PICS.yaml index a960ed661aea04..1fc182bd9ba867 100644 --- a/src/app/tests/suites/certification/PICS.yaml +++ b/src/app/tests/suites/certification/PICS.yaml @@ -10301,6 +10301,17 @@ PICS: - label: "Does the device implement the ActiveEndpoints attribute?" id: PWRTL.S.A0001 + # + # Thread Border Router Management Cluster + # + - label: + "Does the device implement the Thread Border Router Management cluster + as a server?" + id: TBRM.S + + - label: "Does the device support the PanChange feature?" + id: TBRM.S.F00 + # # Thread Network Directory Cluster # diff --git a/src/app/tests/suites/certification/Test_TC_TBRM_2_2.yaml b/src/app/tests/suites/certification/Test_TC_TBRM_2_2.yaml new file mode 100644 index 00000000000000..852e18a73adc8a --- /dev/null +++ b/src/app/tests/suites/certification/Test_TC_TBRM_2_2.yaml @@ -0,0 +1,116 @@ +# 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. + +name: "[TC-TBRM-2.2] Initial Dataset configuration of Thread Border Router" + +PICS: + - TBRM.S + +config: + nodeId: 0x12344321 + cluster: Thread Border Router Management + endpoint: 1 + PIXIT.TBRM.THREAD_ACTIVE_DATASET: + type: octet_string + defaultValue: "hex:0e080000000000010000000300001235060004001fffe002082ad51c02fe8f64f20708fddb8af85255f93a051083e2b9b2cc609b00125adbf823ea2ab20102c4d904100a133626c411d7de02a570ca3c3d80470c0402a0f7f8031054687265616441637469766554657374" + # Active Timestamp ----^^^^^^^^^^^^^^^^ + PIXIT.TBRM.THREAD_ACTIVE_DATASET.ACTIVE_TIMESTAMP: 0x10000 + PIXIT.TBRM.THREAD_INVALID_DATASET: + type: octet_string + defaultValue: "hex:00112233" + +# Precondition: +# The DUT does not have an Active Dataset + +tests: + - label: "Wait for the commissioned device to be retrieved" + cluster: DelayCommands + command: WaitForCommissionee + arguments: + values: + - name: nodeId + value: nodeId + + - label: "TH reads the ActiveDatasetTimestamp attribute from the DUT" + command: readAttribute + attribute: ActiveDatasetTimestamp + response: + value: null + + - label: "TH reads the PendingDatasetTimestamp attribute from the DUT" + command: readAttribute + attribute: PendingDatasetTimestamp + response: + value: null + + - label: + "TH sends a valid ActiveDatasetRequest command to the DUT without + having armed the fail-safe" + command: SetActiveDatasetRequest + arguments: + values: + - name: ActiveDataset + value: PIXIT.TBRM.THREAD_ACTIVE_DATASET + response: + error: FAILSAFE_REQUIRED + + - label: "TH sends ArmFailSafe command to the DUT" + cluster: General Commissioning + command: ArmFailSafe + endpoint: 0 + arguments: + values: + - name: ExpiryLengthSeconds + value: 60 + - name: Breadcrumb + value: 1 + + - label: "TH sends an invalid ActiveDatasetRequest command to the DUT" + command: SetActiveDatasetRequest + arguments: + values: + - name: ActiveDataset + value: PIXIT.TBRM.THREAD_INVALID_DATASET + response: + error: INVALID_COMMAND + + - label: "TH sends a valid ActiveDatasetRequest command to the DUT" + command: SetActiveDatasetRequest + arguments: + values: + - name: ActiveDataset + value: PIXIT.TBRM.THREAD_ACTIVE_DATASET + + - label: "TH reads the InterfaceEnabled attribute from the DUT" + command: readAttribute + attribute: InterfaceEnabled + response: + value: true + + - label: "TH reads the ActiveDatasetTimestamp attribute from the DUT" + command: readAttribute + attribute: ActiveDatasetTimestamp + response: + value: PIXIT.TBRM.THREAD_ACTIVE_DATASET.ACTIVE_TIMESTAMP + constraints: + type: int64u + + - label: "TH sends a valid GetActiveDatasetRequest command to the DUT" + command: GetActiveDatasetRequest + response: + values: + - name: Dataset + value: PIXIT.TBRM.THREAD_ACTIVE_DATASET + constraints: + type: octet_string diff --git a/src/app/tests/suites/certification/Test_TC_TBRM_2_3.yaml b/src/app/tests/suites/certification/Test_TC_TBRM_2_3.yaml new file mode 100644 index 00000000000000..7a55191f471f0b --- /dev/null +++ b/src/app/tests/suites/certification/Test_TC_TBRM_2_3.yaml @@ -0,0 +1,166 @@ +# 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. + +name: "[TC-TBRM-2.3] Change dataset configuration of Thread Border Router" + +PICS: + - TBRM.S + - TBRM.S.F00 + +config: + nodeId: 0x12344321 + cluster: Thread Border Router Management + endpoint: 1 + PIXIT.TBRM.THREAD_ACTIVE_DATASET: + type: octet_string + defaultValue: "hex:0e080000000000010000000300001235060004001fffe002082ad51c02fe8f64f20708fddb8af85255f93a051083e2b9b2cc609b00125adbf823ea2ab20102c4d904100a133626c411d7de02a570ca3c3d80470c0402a0f7f8031054687265616441637469766554657374" + PIXIT.TBRM.THREAD_PENDING_DATASET: + type: octet_string + defaultValue: "hex:0e08000000000002000033080000000000010000340400004e2035060004001fffe002082ad51c02fe8f64f20708fddb8af85255f93a051083e2b9b2cc609b00125adbf823ea2ab20102c4d904100a133626c411d7de02a570ca3c3d80470c0402a0f7f8030d54687265616450656e64696e67000300000c" + # Active Timestamp ----^^^^^^^^^^^^^^^^ + # Pending Timestamp -----------------------^^^^^^^^^^^^^^^^ + # Delay Timer -------------------------------------------------^^^^^^^^ == 20000ms Note: waitForReport has a hard-coded 30 second timeout + +tests: + - label: "Wait for the commissioned device to be retrieved" + cluster: DelayCommands + command: WaitForCommissionee + arguments: + values: + - name: nodeId + value: nodeId + # Step 1 + - label: "TH reads the ActiveDatasetTimestamp attribute from the DUT" + command: readAttribute + attribute: ActiveDatasetTimestamp + response: + saveAs: initialActiveTimestamp + constraints: + type: int64u + + - label: "If the ActiveDatasetTimestamp attribute not null, go to step 4" + cluster: EqualityCommands + command: UnsignedNumberEquals + arguments: + values: + - name: Value1 + value: initialActiveTimestamp + - name: Value2 + value: null + response: + - values: + - name: Equals + saveAs: noActiveDataset + + # Step 2 + - label: "TH sends ArmFailSafe command to the DUT" + runIf: noActiveDataset + cluster: General Commissioning + command: ArmFailSafe + endpoint: 0 + arguments: + values: + - name: ExpiryLengthSeconds + value: 60 + - name: Breadcrumb + value: 1 + + # Step 3 + - label: "TH sends a valid ActiveDatasetRequest command to the DUT" + runIf: noActiveDataset + command: SetActiveDatasetRequest + arguments: + values: + - name: ActiveDataset + value: PIXIT.TBRM.THREAD_ACTIVE_DATASET + + # Step 4 + - label: "TH reads the PendingDatasetTimestamp attribute from the DUT" + command: readAttribute + attribute: PendingDatasetTimestamp + response: + saveAs: initialPendingTimestamp + constraints: + type: int64u + + # Step 5 + - label: "TH sends a SetPendingDatasetRequest command to the DUT" + command: SetPendingDatasetRequest + arguments: + values: + - name: PendingDataset + value: PIXIT.TBRM.THREAD_PENDING_DATASET + + # Step 6 + - label: "TH sends a GetPendingDatasetRequest command to the DUT" + command: GetPendingDatasetRequest + response: + values: + - name: Dataset + constraints: + type: octet_string + # TODO: This should be PIXIT.TBRM.THREAD_PENDING_DATASET but ignoring the Delay Timer element if present + + # Step 7 + - label: "TH reads the PendingDatasetTimestamp attribute from the DUT" + command: readAttribute + attribute: PendingDatasetTimestamp + response: + constraints: + type: int64u + notValue: initialPendingTimestamp + + # Step 8 + - label: + "TH subscribes to the ActiveDatasetTimestamp attribute from the DUT" + command: subscribeAttribute + attribute: ActiveDatasetTimestamp + minInterval: 1 + maxInterval: 70 + response: + constraints: + type: int64u + hasValue: true # not null + + - label: "TH waits for an ActiveDatasetTimestamp report" + command: waitForReport + attribute: ActiveDatasetTimestamp + # TODO: waitForReport uses a hard-coded timeout of 30s, should be configurable? + response: + constraints: + hasValue: true + + # Step 9 + - label: "TH reads the PendingDatasetTimestamp attribute from the DUT" + command: readAttribute + attribute: PendingDatasetTimestamp + response: + value: null + + # Step 10 + - label: "TH sends a valid GetActiveDatasetRequest command to the DUT" + command: GetActiveDatasetRequest + response: + values: + - name: Dataset + # TODO: This should be PIXIT.TBRM.THREAD_PENDING_DATASET without the Delay Timer and Pending Timestamp elements + constraints: + type: octet_string + + # Step 11 + - label: "TH reads the InterfaceEnabled attribute from the DUT" + command: readAttribute + attribute: InterfaceEnabled + response: + value: true diff --git a/src/app/tests/suites/certification/ci-pics-values b/src/app/tests/suites/certification/ci-pics-values index 6763104e376603..f64941bb688890 100644 --- a/src/app/tests/suites/certification/ci-pics-values +++ b/src/app/tests/suites/certification/ci-pics-values @@ -3018,6 +3018,10 @@ PWRTL.S.F01=0 PWRTL.S.F02=1 PWRTL.S.F03=1 +# Thread Border Router Management Cluster +TBRM.S=1 +TBRM.S.F00=1 + # Thread Network Directory Cluster THNETDIR.S=1 diff --git a/src/app/zap-templates/zcl/data-model/chip/water-heater-management-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/water-heater-management-cluster.xml index 0c5a9a1efea2fa..8246930e5b4dab 100644 --- a/src/app/zap-templates/zcl/data-model/chip/water-heater-management-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/water-heater-management-cluster.xml @@ -16,16 +16,7 @@ limitations under the License. --> - - - - - - - - - - + @@ -40,6 +31,16 @@ limitations under the License. + + + + + + + + + + Energy Management Water Heater Management @@ -51,26 +52,21 @@ limitations under the License. - + true - - HeaterTypes - HeatDemand + + HeaterTypes + HeatDemand TankVolume - EstimatedHeatRequired + EstimatedHeatRequired TankPercentage BoostState Allows a client to request that the water heater is put into a Boost state. - - - - - - + @@ -79,5 +75,14 @@ limitations under the License. + + BoostStarted + + + + + BoostEnded + + diff --git a/src/controller/data_model/controller-clusters.matter b/src/controller/data_model/controller-clusters.matter index 2e41cad5a77fe2..02408e1ecf74b0 100644 --- a/src/controller/data_model/controller-clusters.matter +++ b/src/controller/data_model/controller-clusters.matter @@ -4647,7 +4647,7 @@ cluster ElectricalEnergyMeasurement = 145 { /** This cluster is used to allow clients to control the operation of a hot water heating appliance so that it can be used with energy management. */ provisional cluster WaterHeaterManagement = 148 { - revision 1; + revision 2; enum BoostStateEnum : enum8 { kInactive = 0; @@ -4659,7 +4659,7 @@ provisional cluster WaterHeaterManagement = 148 { kTankPercent = 0x2; } - bitmap WaterHeaterDemandBitmap : bitmap8 { + bitmap WaterHeaterHeatSourceBitmap : bitmap8 { kImmersionElement1 = 0x1; kImmersionElement2 = 0x2; kHeatPump = 0x4; @@ -4667,16 +4667,24 @@ provisional cluster WaterHeaterManagement = 148 { kOther = 0x10; } - bitmap WaterHeaterTypeBitmap : bitmap8 { - kImmersionElement1 = 0x1; - kImmersionElement2 = 0x2; - kHeatPump = 0x4; - kBoiler = 0x8; - kOther = 0x10; + struct WaterHeaterBoostInfoStruct { + elapsed_s duration = 0; + optional boolean oneShot = 1; + optional boolean emergencyBoost = 2; + optional temperature temporarySetpoint = 3; + optional percent targetPercentage = 4; + optional percent targetReheat = 5; + } + + info event BoostStarted = 0 { + WaterHeaterBoostInfoStruct boostInfo = 0; } - readonly attribute WaterHeaterTypeBitmap heaterTypes = 0; - readonly attribute WaterHeaterDemandBitmap heatDemand = 1; + info event BoostEnded = 1 { + } + + readonly attribute WaterHeaterHeatSourceBitmap heaterTypes = 0; + readonly attribute WaterHeaterHeatSourceBitmap heatDemand = 1; readonly attribute optional int16u tankVolume = 2; readonly attribute optional energy_mwh estimatedHeatRequired = 3; readonly attribute optional percent tankPercentage = 4; @@ -4689,12 +4697,7 @@ provisional cluster WaterHeaterManagement = 148 { readonly attribute int16u clusterRevision = 65533; request struct BoostRequest { - elapsed_s duration = 0; - optional boolean oneShot = 1; - optional boolean emergencyBoost = 2; - optional temperature temporarySetpoint = 3; - optional percent targetPercentage = 4; - optional percent targetReheat = 5; + WaterHeaterBoostInfoStruct boostInfo = 0; } /** Allows a client to request that the water heater is put into a Boost state. */ diff --git a/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java b/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java index 5944e05356d9c5..8b112f53c6f36d 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ChipClusters.java @@ -30541,37 +30541,17 @@ public long initWithDevice(long devicePtr, int endpointId) { return 0L; } - public void boost(DefaultClusterCallback callback, Long duration, Optional oneShot, Optional emergencyBoost, Optional temporarySetpoint, Optional targetPercentage, Optional targetReheat) { - boost(callback, duration, oneShot, emergencyBoost, temporarySetpoint, targetPercentage, targetReheat, 0); + public void boost(DefaultClusterCallback callback, ChipStructs.WaterHeaterManagementClusterWaterHeaterBoostInfoStruct boostInfo) { + boost(callback, boostInfo, 0); } - public void boost(DefaultClusterCallback callback, Long duration, Optional oneShot, Optional emergencyBoost, Optional temporarySetpoint, Optional targetPercentage, Optional targetReheat, int timedInvokeTimeoutMs) { + public void boost(DefaultClusterCallback callback, ChipStructs.WaterHeaterManagementClusterWaterHeaterBoostInfoStruct boostInfo, int timedInvokeTimeoutMs) { final long commandId = 0L; ArrayList elements = new ArrayList<>(); - final long durationFieldID = 0L; - BaseTLVType durationtlvValue = new UIntType(duration); - elements.add(new StructElement(durationFieldID, durationtlvValue)); - - final long oneShotFieldID = 1L; - BaseTLVType oneShottlvValue = oneShot.map((nonOptionaloneShot) -> new BooleanType(nonOptionaloneShot)).orElse(new EmptyType()); - elements.add(new StructElement(oneShotFieldID, oneShottlvValue)); - - final long emergencyBoostFieldID = 2L; - BaseTLVType emergencyBoosttlvValue = emergencyBoost.map((nonOptionalemergencyBoost) -> new BooleanType(nonOptionalemergencyBoost)).orElse(new EmptyType()); - elements.add(new StructElement(emergencyBoostFieldID, emergencyBoosttlvValue)); - - final long temporarySetpointFieldID = 3L; - BaseTLVType temporarySetpointtlvValue = temporarySetpoint.map((nonOptionaltemporarySetpoint) -> new IntType(nonOptionaltemporarySetpoint)).orElse(new EmptyType()); - elements.add(new StructElement(temporarySetpointFieldID, temporarySetpointtlvValue)); - - final long targetPercentageFieldID = 4L; - BaseTLVType targetPercentagetlvValue = targetPercentage.map((nonOptionaltargetPercentage) -> new UIntType(nonOptionaltargetPercentage)).orElse(new EmptyType()); - elements.add(new StructElement(targetPercentageFieldID, targetPercentagetlvValue)); - - final long targetReheatFieldID = 5L; - BaseTLVType targetReheattlvValue = targetReheat.map((nonOptionaltargetReheat) -> new UIntType(nonOptionaltargetReheat)).orElse(new EmptyType()); - elements.add(new StructElement(targetReheatFieldID, targetReheattlvValue)); + final long boostInfoFieldID = 0L; + BaseTLVType boostInfotlvValue = boostInfo.encodeTlv(); + elements.add(new StructElement(boostInfoFieldID, boostInfotlvValue)); StructType commandArgs = new StructType(elements); invoke(new InvokeCallbackImpl(callback) { diff --git a/src/controller/java/generated/java/chip/devicecontroller/ChipEventStructs.java b/src/controller/java/generated/java/chip/devicecontroller/ChipEventStructs.java index 7a7634b395cb38..4e3c6ce296c7b7 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ChipEventStructs.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ChipEventStructs.java @@ -3808,6 +3808,80 @@ public String toString() { return output.toString(); } } +public static class WaterHeaterManagementClusterBoostStartedEvent { + public ChipStructs.WaterHeaterManagementClusterWaterHeaterBoostInfoStruct boostInfo; + private static final long BOOST_INFO_ID = 0L; + + public WaterHeaterManagementClusterBoostStartedEvent( + ChipStructs.WaterHeaterManagementClusterWaterHeaterBoostInfoStruct boostInfo + ) { + this.boostInfo = boostInfo; + } + + public StructType encodeTlv() { + ArrayList values = new ArrayList<>(); + values.add(new StructElement(BOOST_INFO_ID, boostInfo.encodeTlv())); + + return new StructType(values); + } + + public static WaterHeaterManagementClusterBoostStartedEvent decodeTlv(BaseTLVType tlvValue) { + if (tlvValue == null || tlvValue.type() != TLVType.Struct) { + return null; + } + ChipStructs.WaterHeaterManagementClusterWaterHeaterBoostInfoStruct boostInfo = null; + for (StructElement element: ((StructType)tlvValue).value()) { + if (element.contextTagNum() == BOOST_INFO_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.Struct) { + StructType castingValue = element.value(StructType.class); + boostInfo = ChipStructs.WaterHeaterManagementClusterWaterHeaterBoostInfoStruct.decodeTlv(castingValue); + } + } + } + return new WaterHeaterManagementClusterBoostStartedEvent( + boostInfo + ); + } + + @Override + public String toString() { + StringBuilder output = new StringBuilder(); + output.append("WaterHeaterManagementClusterBoostStartedEvent {\n"); + output.append("\tboostInfo: "); + output.append(boostInfo); + output.append("\n"); + output.append("}\n"); + return output.toString(); + } +} +public static class WaterHeaterManagementClusterBoostEndedEvent { + + public WaterHeaterManagementClusterBoostEndedEvent( + ) { + } + + public StructType encodeTlv() { + ArrayList values = new ArrayList<>(); + + return new StructType(values); + } + + public static WaterHeaterManagementClusterBoostEndedEvent decodeTlv(BaseTLVType tlvValue) { + if (tlvValue == null || tlvValue.type() != TLVType.Struct) { + return null; + } + return new WaterHeaterManagementClusterBoostEndedEvent( + ); + } + + @Override + public String toString() { + StringBuilder output = new StringBuilder(); + output.append("WaterHeaterManagementClusterBoostEndedEvent {\n"); + output.append("}\n"); + return output.toString(); + } +} public static class DemandResponseLoadControlClusterLoadControlEventStatusChangeEvent { public byte[] eventID; public @Nullable Integer transitionIndex; diff --git a/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java b/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java index 4e07b6d86182d9..2871071475fe69 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ChipStructs.java @@ -6744,6 +6744,127 @@ public String toString() { return output.toString(); } } +public static class WaterHeaterManagementClusterWaterHeaterBoostInfoStruct { + public Long duration; + public Optional oneShot; + public Optional emergencyBoost; + public Optional temporarySetpoint; + public Optional targetPercentage; + public Optional targetReheat; + private static final long DURATION_ID = 0L; + private static final long ONE_SHOT_ID = 1L; + private static final long EMERGENCY_BOOST_ID = 2L; + private static final long TEMPORARY_SETPOINT_ID = 3L; + private static final long TARGET_PERCENTAGE_ID = 4L; + private static final long TARGET_REHEAT_ID = 5L; + + public WaterHeaterManagementClusterWaterHeaterBoostInfoStruct( + Long duration, + Optional oneShot, + Optional emergencyBoost, + Optional temporarySetpoint, + Optional targetPercentage, + Optional targetReheat + ) { + this.duration = duration; + this.oneShot = oneShot; + this.emergencyBoost = emergencyBoost; + this.temporarySetpoint = temporarySetpoint; + this.targetPercentage = targetPercentage; + this.targetReheat = targetReheat; + } + + public StructType encodeTlv() { + ArrayList values = new ArrayList<>(); + values.add(new StructElement(DURATION_ID, new UIntType(duration))); + values.add(new StructElement(ONE_SHOT_ID, oneShot.map((nonOptionaloneShot) -> new BooleanType(nonOptionaloneShot)).orElse(new EmptyType()))); + values.add(new StructElement(EMERGENCY_BOOST_ID, emergencyBoost.map((nonOptionalemergencyBoost) -> new BooleanType(nonOptionalemergencyBoost)).orElse(new EmptyType()))); + values.add(new StructElement(TEMPORARY_SETPOINT_ID, temporarySetpoint.map((nonOptionaltemporarySetpoint) -> new IntType(nonOptionaltemporarySetpoint)).orElse(new EmptyType()))); + values.add(new StructElement(TARGET_PERCENTAGE_ID, targetPercentage.map((nonOptionaltargetPercentage) -> new UIntType(nonOptionaltargetPercentage)).orElse(new EmptyType()))); + values.add(new StructElement(TARGET_REHEAT_ID, targetReheat.map((nonOptionaltargetReheat) -> new UIntType(nonOptionaltargetReheat)).orElse(new EmptyType()))); + + return new StructType(values); + } + + public static WaterHeaterManagementClusterWaterHeaterBoostInfoStruct decodeTlv(BaseTLVType tlvValue) { + if (tlvValue == null || tlvValue.type() != TLVType.Struct) { + return null; + } + Long duration = null; + Optional oneShot = Optional.empty(); + Optional emergencyBoost = Optional.empty(); + Optional temporarySetpoint = Optional.empty(); + Optional targetPercentage = Optional.empty(); + Optional targetReheat = Optional.empty(); + for (StructElement element: ((StructType)tlvValue).value()) { + if (element.contextTagNum() == DURATION_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.UInt) { + UIntType castingValue = element.value(UIntType.class); + duration = castingValue.value(Long.class); + } + } else if (element.contextTagNum() == ONE_SHOT_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.Boolean) { + BooleanType castingValue = element.value(BooleanType.class); + oneShot = Optional.of(castingValue.value(Boolean.class)); + } + } else if (element.contextTagNum() == EMERGENCY_BOOST_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.Boolean) { + BooleanType castingValue = element.value(BooleanType.class); + emergencyBoost = Optional.of(castingValue.value(Boolean.class)); + } + } else if (element.contextTagNum() == TEMPORARY_SETPOINT_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.Int) { + IntType castingValue = element.value(IntType.class); + temporarySetpoint = Optional.of(castingValue.value(Integer.class)); + } + } else if (element.contextTagNum() == TARGET_PERCENTAGE_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.UInt) { + UIntType castingValue = element.value(UIntType.class); + targetPercentage = Optional.of(castingValue.value(Integer.class)); + } + } else if (element.contextTagNum() == TARGET_REHEAT_ID) { + if (element.value(BaseTLVType.class).type() == TLVType.UInt) { + UIntType castingValue = element.value(UIntType.class); + targetReheat = Optional.of(castingValue.value(Integer.class)); + } + } + } + return new WaterHeaterManagementClusterWaterHeaterBoostInfoStruct( + duration, + oneShot, + emergencyBoost, + temporarySetpoint, + targetPercentage, + targetReheat + ); + } + + @Override + public String toString() { + StringBuilder output = new StringBuilder(); + output.append("WaterHeaterManagementClusterWaterHeaterBoostInfoStruct {\n"); + output.append("\tduration: "); + output.append(duration); + output.append("\n"); + output.append("\toneShot: "); + output.append(oneShot); + output.append("\n"); + output.append("\temergencyBoost: "); + output.append(emergencyBoost); + output.append("\n"); + output.append("\ttemporarySetpoint: "); + output.append(temporarySetpoint); + output.append("\n"); + output.append("\ttargetPercentage: "); + output.append(targetPercentage); + output.append("\n"); + output.append("\ttargetReheat: "); + output.append(targetReheat); + output.append("\n"); + output.append("}\n"); + return output.toString(); + } +} public static class DemandResponseLoadControlClusterHeatingSourceControlStruct { public Integer heatingSource; private static final long HEATING_SOURCE_ID = 0L; diff --git a/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java index c6677b6aa93f47..5dbefbab1da2b7 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterIDMapping.java @@ -9415,7 +9415,9 @@ public static Attribute value(long id) throws NoSuchFieldError { } } - public enum Event {; + public enum Event { + BoostStarted(0L), + BoostEnded(1L),; private final long id; Event(long id) { this.id = id; @@ -9455,7 +9457,7 @@ public static Command value(long id) throws NoSuchFieldError { } throw new NoSuchFieldError(); } - }public enum BoostCommandField {Duration(0),OneShot(1),EmergencyBoost(2),TemporarySetpoint(3),TargetPercentage(4),TargetReheat(5),; + }public enum BoostCommandField {BoostInfo(0),; private final int id; BoostCommandField(int id) { this.id = id; diff --git a/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java b/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java index 986d59f3635af1..f58ac0f6e47798 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java +++ b/src/controller/java/generated/java/chip/devicecontroller/ClusterInfoMapping.java @@ -25754,39 +25754,12 @@ public Map> getCommandMap() { Map waterHeaterManagementboostCommandParams = new LinkedHashMap(); - CommandParameterInfo waterHeaterManagementboostdurationCommandParameterInfo = new CommandParameterInfo("duration", Long.class, Long.class); - waterHeaterManagementboostCommandParams.put("duration",waterHeaterManagementboostdurationCommandParameterInfo); - - CommandParameterInfo waterHeaterManagementboostoneShotCommandParameterInfo = new CommandParameterInfo("oneShot", Optional.class, Boolean.class); - waterHeaterManagementboostCommandParams.put("oneShot",waterHeaterManagementboostoneShotCommandParameterInfo); - - CommandParameterInfo waterHeaterManagementboostemergencyBoostCommandParameterInfo = new CommandParameterInfo("emergencyBoost", Optional.class, Boolean.class); - waterHeaterManagementboostCommandParams.put("emergencyBoost",waterHeaterManagementboostemergencyBoostCommandParameterInfo); - - CommandParameterInfo waterHeaterManagementboosttemporarySetpointCommandParameterInfo = new CommandParameterInfo("temporarySetpoint", Optional.class, Integer.class); - waterHeaterManagementboostCommandParams.put("temporarySetpoint",waterHeaterManagementboosttemporarySetpointCommandParameterInfo); - - CommandParameterInfo waterHeaterManagementboosttargetPercentageCommandParameterInfo = new CommandParameterInfo("targetPercentage", Optional.class, Integer.class); - waterHeaterManagementboostCommandParams.put("targetPercentage",waterHeaterManagementboosttargetPercentageCommandParameterInfo); - - CommandParameterInfo waterHeaterManagementboosttargetReheatCommandParameterInfo = new CommandParameterInfo("targetReheat", Optional.class, Integer.class); - waterHeaterManagementboostCommandParams.put("targetReheat",waterHeaterManagementboosttargetReheatCommandParameterInfo); InteractionInfo waterHeaterManagementboostInteractionInfo = new InteractionInfo( (cluster, callback, commandArguments) -> { ((ChipClusters.WaterHeaterManagementCluster) cluster) .boost((DefaultClusterCallback) callback - , (Long) - commandArguments.get("duration") - , (Optional) - commandArguments.get("oneShot") - , (Optional) - commandArguments.get("emergencyBoost") - , (Optional) - commandArguments.get("temporarySetpoint") - , (Optional) - commandArguments.get("targetPercentage") - , (Optional) - commandArguments.get("targetReheat") + , (ChipStructs.WaterHeaterManagementClusterWaterHeaterBoostInfoStruct) + commandArguments.get("boostInfo") ); }, () -> new DelegatedDefaultClusterCallback(), diff --git a/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/WaterHeaterManagementClusterBoostStartedEvent.kt b/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/WaterHeaterManagementClusterBoostStartedEvent.kt new file mode 100644 index 00000000000000..f8a5e55e2e7894 --- /dev/null +++ b/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/WaterHeaterManagementClusterBoostStartedEvent.kt @@ -0,0 +1,57 @@ +/* + * + * 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.eventstructs + +import chip.devicecontroller.cluster.* +import matter.tlv.ContextSpecificTag +import matter.tlv.Tag +import matter.tlv.TlvReader +import matter.tlv.TlvWriter + +class WaterHeaterManagementClusterBoostStartedEvent( + val boostInfo: + chip.devicecontroller.cluster.structs.WaterHeaterManagementClusterWaterHeaterBoostInfoStruct +) { + override fun toString(): String = buildString { + append("WaterHeaterManagementClusterBoostStartedEvent {\n") + append("\tboostInfo : $boostInfo\n") + append("}\n") + } + + fun toTlv(tlvTag: Tag, tlvWriter: TlvWriter) { + tlvWriter.apply { + startStructure(tlvTag) + boostInfo.toTlv(ContextSpecificTag(TAG_BOOST_INFO), this) + endStructure() + } + } + + companion object { + private const val TAG_BOOST_INFO = 0 + + fun fromTlv(tlvTag: Tag, tlvReader: TlvReader): WaterHeaterManagementClusterBoostStartedEvent { + tlvReader.enterStructure(tlvTag) + val boostInfo = + chip.devicecontroller.cluster.structs.WaterHeaterManagementClusterWaterHeaterBoostInfoStruct + .fromTlv(ContextSpecificTag(TAG_BOOST_INFO), tlvReader) + + tlvReader.exitContainer() + + return WaterHeaterManagementClusterBoostStartedEvent(boostInfo) + } + } +} 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 ca6223a2c1f96e..9665346b3d04b2 100644 --- a/src/controller/java/generated/java/chip/devicecontroller/cluster/files.gni +++ b/src/controller/java/generated/java/chip/devicecontroller/cluster/files.gni @@ -156,6 +156,7 @@ structs_sources = [ "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/UnitTestingClusterTestGlobalStruct.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/UnitTestingClusterTestListStructOctet.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/UserLabelClusterLabelStruct.kt", + "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/WaterHeaterManagementClusterWaterHeaterBoostInfoStruct.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/WaterHeaterModeClusterModeOptionStruct.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/WaterHeaterModeClusterModeTagStruct.kt", ] @@ -241,6 +242,7 @@ eventstructs_sources = [ "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/UnitTestingClusterTestFabricScopedEventEvent.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/ValveConfigurationAndControlClusterValveFaultEvent.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/ValveConfigurationAndControlClusterValveStateChangedEvent.kt", + "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/WaterHeaterManagementClusterBoostStartedEvent.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/WiFiNetworkDiagnosticsClusterAssociationFailureEvent.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/WiFiNetworkDiagnosticsClusterConnectionStatusEvent.kt", "${chip_root}/src/controller/java/generated/java/chip/devicecontroller/cluster/eventstructs/WiFiNetworkDiagnosticsClusterDisconnectionEvent.kt", diff --git a/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/WaterHeaterManagementClusterWaterHeaterBoostInfoStruct.kt b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/WaterHeaterManagementClusterWaterHeaterBoostInfoStruct.kt new file mode 100644 index 00000000000000..c9f95799bd18ba --- /dev/null +++ b/src/controller/java/generated/java/chip/devicecontroller/cluster/structs/WaterHeaterManagementClusterWaterHeaterBoostInfoStruct.kt @@ -0,0 +1,130 @@ +/* + * + * 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 WaterHeaterManagementClusterWaterHeaterBoostInfoStruct( + val duration: ULong, + val oneShot: Optional, + val emergencyBoost: Optional, + val temporarySetpoint: Optional, + val targetPercentage: Optional, + val targetReheat: Optional, +) { + override fun toString(): String = buildString { + append("WaterHeaterManagementClusterWaterHeaterBoostInfoStruct {\n") + append("\tduration : $duration\n") + append("\toneShot : $oneShot\n") + append("\temergencyBoost : $emergencyBoost\n") + append("\ttemporarySetpoint : $temporarySetpoint\n") + append("\ttargetPercentage : $targetPercentage\n") + append("\ttargetReheat : $targetReheat\n") + append("}\n") + } + + fun toTlv(tlvTag: Tag, tlvWriter: TlvWriter) { + tlvWriter.apply { + startStructure(tlvTag) + put(ContextSpecificTag(TAG_DURATION), duration) + if (oneShot.isPresent) { + val optoneShot = oneShot.get() + put(ContextSpecificTag(TAG_ONE_SHOT), optoneShot) + } + if (emergencyBoost.isPresent) { + val optemergencyBoost = emergencyBoost.get() + put(ContextSpecificTag(TAG_EMERGENCY_BOOST), optemergencyBoost) + } + if (temporarySetpoint.isPresent) { + val opttemporarySetpoint = temporarySetpoint.get() + put(ContextSpecificTag(TAG_TEMPORARY_SETPOINT), opttemporarySetpoint) + } + if (targetPercentage.isPresent) { + val opttargetPercentage = targetPercentage.get() + put(ContextSpecificTag(TAG_TARGET_PERCENTAGE), opttargetPercentage) + } + if (targetReheat.isPresent) { + val opttargetReheat = targetReheat.get() + put(ContextSpecificTag(TAG_TARGET_REHEAT), opttargetReheat) + } + endStructure() + } + } + + companion object { + private const val TAG_DURATION = 0 + private const val TAG_ONE_SHOT = 1 + private const val TAG_EMERGENCY_BOOST = 2 + private const val TAG_TEMPORARY_SETPOINT = 3 + private const val TAG_TARGET_PERCENTAGE = 4 + private const val TAG_TARGET_REHEAT = 5 + + fun fromTlv( + tlvTag: Tag, + tlvReader: TlvReader, + ): WaterHeaterManagementClusterWaterHeaterBoostInfoStruct { + tlvReader.enterStructure(tlvTag) + val duration = tlvReader.getULong(ContextSpecificTag(TAG_DURATION)) + val oneShot = + if (tlvReader.isNextTag(ContextSpecificTag(TAG_ONE_SHOT))) { + Optional.of(tlvReader.getBoolean(ContextSpecificTag(TAG_ONE_SHOT))) + } else { + Optional.empty() + } + val emergencyBoost = + if (tlvReader.isNextTag(ContextSpecificTag(TAG_EMERGENCY_BOOST))) { + Optional.of(tlvReader.getBoolean(ContextSpecificTag(TAG_EMERGENCY_BOOST))) + } else { + Optional.empty() + } + val temporarySetpoint = + if (tlvReader.isNextTag(ContextSpecificTag(TAG_TEMPORARY_SETPOINT))) { + Optional.of(tlvReader.getInt(ContextSpecificTag(TAG_TEMPORARY_SETPOINT))) + } else { + Optional.empty() + } + val targetPercentage = + if (tlvReader.isNextTag(ContextSpecificTag(TAG_TARGET_PERCENTAGE))) { + Optional.of(tlvReader.getUInt(ContextSpecificTag(TAG_TARGET_PERCENTAGE))) + } else { + Optional.empty() + } + val targetReheat = + if (tlvReader.isNextTag(ContextSpecificTag(TAG_TARGET_REHEAT))) { + Optional.of(tlvReader.getUInt(ContextSpecificTag(TAG_TARGET_REHEAT))) + } else { + Optional.empty() + } + + tlvReader.exitContainer() + + return WaterHeaterManagementClusterWaterHeaterBoostInfoStruct( + duration, + oneShot, + emergencyBoost, + temporarySetpoint, + targetPercentage, + targetReheat, + ) + } + } +} diff --git a/src/controller/java/generated/java/matter/controller/cluster/clusters/WaterHeaterManagementCluster.kt b/src/controller/java/generated/java/matter/controller/cluster/clusters/WaterHeaterManagementCluster.kt index 7d0ce89e2a612e..5f7fb25dd58f77 100644 --- a/src/controller/java/generated/java/matter/controller/cluster/clusters/WaterHeaterManagementCluster.kt +++ b/src/controller/java/generated/java/matter/controller/cluster/clusters/WaterHeaterManagementCluster.kt @@ -86,12 +86,7 @@ class WaterHeaterManagementCluster( } suspend fun boost( - duration: UInt, - oneShot: Boolean?, - emergencyBoost: Boolean?, - temporarySetpoint: Short?, - targetPercentage: UByte?, - targetReheat: UByte?, + boostInfo: WaterHeaterManagementClusterWaterHeaterBoostInfoStruct, timedInvokeTimeout: Duration? = null, ) { val commandId: UInt = 0u @@ -99,29 +94,8 @@ class WaterHeaterManagementCluster( val tlvWriter = TlvWriter() tlvWriter.startStructure(AnonymousTag) - val TAG_DURATION_REQ: Int = 0 - tlvWriter.put(ContextSpecificTag(TAG_DURATION_REQ), duration) - - val TAG_ONE_SHOT_REQ: Int = 1 - oneShot?.let { tlvWriter.put(ContextSpecificTag(TAG_ONE_SHOT_REQ), oneShot) } - - val TAG_EMERGENCY_BOOST_REQ: Int = 2 - emergencyBoost?.let { - tlvWriter.put(ContextSpecificTag(TAG_EMERGENCY_BOOST_REQ), emergencyBoost) - } - - val TAG_TEMPORARY_SETPOINT_REQ: Int = 3 - temporarySetpoint?.let { - tlvWriter.put(ContextSpecificTag(TAG_TEMPORARY_SETPOINT_REQ), temporarySetpoint) - } - - val TAG_TARGET_PERCENTAGE_REQ: Int = 4 - targetPercentage?.let { - tlvWriter.put(ContextSpecificTag(TAG_TARGET_PERCENTAGE_REQ), targetPercentage) - } - - val TAG_TARGET_REHEAT_REQ: Int = 5 - targetReheat?.let { tlvWriter.put(ContextSpecificTag(TAG_TARGET_REHEAT_REQ), targetReheat) } + val TAG_BOOST_INFO_REQ: Int = 0 + boostInfo.toTlv(ContextSpecificTag(TAG_BOOST_INFO_REQ), tlvWriter) tlvWriter.endStructure() val request: InvokeRequest = diff --git a/src/controller/java/generated/java/matter/controller/cluster/eventstructs/WaterHeaterManagementClusterBoostStartedEvent.kt b/src/controller/java/generated/java/matter/controller/cluster/eventstructs/WaterHeaterManagementClusterBoostStartedEvent.kt new file mode 100644 index 00000000000000..44e51796c1fa50 --- /dev/null +++ b/src/controller/java/generated/java/matter/controller/cluster/eventstructs/WaterHeaterManagementClusterBoostStartedEvent.kt @@ -0,0 +1,57 @@ +/* + * + * 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.eventstructs + +import matter.controller.cluster.* +import matter.tlv.ContextSpecificTag +import matter.tlv.Tag +import matter.tlv.TlvReader +import matter.tlv.TlvWriter + +class WaterHeaterManagementClusterBoostStartedEvent( + val boostInfo: + matter.controller.cluster.structs.WaterHeaterManagementClusterWaterHeaterBoostInfoStruct +) { + override fun toString(): String = buildString { + append("WaterHeaterManagementClusterBoostStartedEvent {\n") + append("\tboostInfo : $boostInfo\n") + append("}\n") + } + + fun toTlv(tlvTag: Tag, tlvWriter: TlvWriter) { + tlvWriter.apply { + startStructure(tlvTag) + boostInfo.toTlv(ContextSpecificTag(TAG_BOOST_INFO), this) + endStructure() + } + } + + companion object { + private const val TAG_BOOST_INFO = 0 + + fun fromTlv(tlvTag: Tag, tlvReader: TlvReader): WaterHeaterManagementClusterBoostStartedEvent { + tlvReader.enterStructure(tlvTag) + val boostInfo = + matter.controller.cluster.structs.WaterHeaterManagementClusterWaterHeaterBoostInfoStruct + .fromTlv(ContextSpecificTag(TAG_BOOST_INFO), tlvReader) + + tlvReader.exitContainer() + + return WaterHeaterManagementClusterBoostStartedEvent(boostInfo) + } + } +} 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 844c6692f5b9ad..62769c99e0bb89 100644 --- a/src/controller/java/generated/java/matter/controller/cluster/files.gni +++ b/src/controller/java/generated/java/matter/controller/cluster/files.gni @@ -156,6 +156,7 @@ matter_structs_sources = [ "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/UnitTestingClusterTestGlobalStruct.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/UnitTestingClusterTestListStructOctet.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/UserLabelClusterLabelStruct.kt", + "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/WaterHeaterManagementClusterWaterHeaterBoostInfoStruct.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/WaterHeaterModeClusterModeOptionStruct.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/structs/WaterHeaterModeClusterModeTagStruct.kt", ] @@ -241,6 +242,7 @@ matter_eventstructs_sources = [ "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/eventstructs/UnitTestingClusterTestFabricScopedEventEvent.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/eventstructs/ValveConfigurationAndControlClusterValveFaultEvent.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/eventstructs/ValveConfigurationAndControlClusterValveStateChangedEvent.kt", + "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/eventstructs/WaterHeaterManagementClusterBoostStartedEvent.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/eventstructs/WiFiNetworkDiagnosticsClusterAssociationFailureEvent.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/eventstructs/WiFiNetworkDiagnosticsClusterConnectionStatusEvent.kt", "${chip_root}/src/controller/java/generated/java/matter/controller/cluster/eventstructs/WiFiNetworkDiagnosticsClusterDisconnectionEvent.kt", diff --git a/src/controller/java/generated/java/matter/controller/cluster/structs/WaterHeaterManagementClusterWaterHeaterBoostInfoStruct.kt b/src/controller/java/generated/java/matter/controller/cluster/structs/WaterHeaterManagementClusterWaterHeaterBoostInfoStruct.kt new file mode 100644 index 00000000000000..0b2fede4f1537a --- /dev/null +++ b/src/controller/java/generated/java/matter/controller/cluster/structs/WaterHeaterManagementClusterWaterHeaterBoostInfoStruct.kt @@ -0,0 +1,130 @@ +/* + * + * 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 WaterHeaterManagementClusterWaterHeaterBoostInfoStruct( + val duration: UInt, + val oneShot: Optional, + val emergencyBoost: Optional, + val temporarySetpoint: Optional, + val targetPercentage: Optional, + val targetReheat: Optional, +) { + override fun toString(): String = buildString { + append("WaterHeaterManagementClusterWaterHeaterBoostInfoStruct {\n") + append("\tduration : $duration\n") + append("\toneShot : $oneShot\n") + append("\temergencyBoost : $emergencyBoost\n") + append("\ttemporarySetpoint : $temporarySetpoint\n") + append("\ttargetPercentage : $targetPercentage\n") + append("\ttargetReheat : $targetReheat\n") + append("}\n") + } + + fun toTlv(tlvTag: Tag, tlvWriter: TlvWriter) { + tlvWriter.apply { + startStructure(tlvTag) + put(ContextSpecificTag(TAG_DURATION), duration) + if (oneShot.isPresent) { + val optoneShot = oneShot.get() + put(ContextSpecificTag(TAG_ONE_SHOT), optoneShot) + } + if (emergencyBoost.isPresent) { + val optemergencyBoost = emergencyBoost.get() + put(ContextSpecificTag(TAG_EMERGENCY_BOOST), optemergencyBoost) + } + if (temporarySetpoint.isPresent) { + val opttemporarySetpoint = temporarySetpoint.get() + put(ContextSpecificTag(TAG_TEMPORARY_SETPOINT), opttemporarySetpoint) + } + if (targetPercentage.isPresent) { + val opttargetPercentage = targetPercentage.get() + put(ContextSpecificTag(TAG_TARGET_PERCENTAGE), opttargetPercentage) + } + if (targetReheat.isPresent) { + val opttargetReheat = targetReheat.get() + put(ContextSpecificTag(TAG_TARGET_REHEAT), opttargetReheat) + } + endStructure() + } + } + + companion object { + private const val TAG_DURATION = 0 + private const val TAG_ONE_SHOT = 1 + private const val TAG_EMERGENCY_BOOST = 2 + private const val TAG_TEMPORARY_SETPOINT = 3 + private const val TAG_TARGET_PERCENTAGE = 4 + private const val TAG_TARGET_REHEAT = 5 + + fun fromTlv( + tlvTag: Tag, + tlvReader: TlvReader, + ): WaterHeaterManagementClusterWaterHeaterBoostInfoStruct { + tlvReader.enterStructure(tlvTag) + val duration = tlvReader.getUInt(ContextSpecificTag(TAG_DURATION)) + val oneShot = + if (tlvReader.isNextTag(ContextSpecificTag(TAG_ONE_SHOT))) { + Optional.of(tlvReader.getBoolean(ContextSpecificTag(TAG_ONE_SHOT))) + } else { + Optional.empty() + } + val emergencyBoost = + if (tlvReader.isNextTag(ContextSpecificTag(TAG_EMERGENCY_BOOST))) { + Optional.of(tlvReader.getBoolean(ContextSpecificTag(TAG_EMERGENCY_BOOST))) + } else { + Optional.empty() + } + val temporarySetpoint = + if (tlvReader.isNextTag(ContextSpecificTag(TAG_TEMPORARY_SETPOINT))) { + Optional.of(tlvReader.getShort(ContextSpecificTag(TAG_TEMPORARY_SETPOINT))) + } else { + Optional.empty() + } + val targetPercentage = + if (tlvReader.isNextTag(ContextSpecificTag(TAG_TARGET_PERCENTAGE))) { + Optional.of(tlvReader.getUByte(ContextSpecificTag(TAG_TARGET_PERCENTAGE))) + } else { + Optional.empty() + } + val targetReheat = + if (tlvReader.isNextTag(ContextSpecificTag(TAG_TARGET_REHEAT))) { + Optional.of(tlvReader.getUByte(ContextSpecificTag(TAG_TARGET_REHEAT))) + } else { + Optional.empty() + } + + tlvReader.exitContainer() + + return WaterHeaterManagementClusterWaterHeaterBoostInfoStruct( + duration, + oneShot, + emergencyBoost, + temporarySetpoint, + targetPercentage, + targetReheat, + ) + } + } +} diff --git a/src/controller/java/zap-generated/CHIPEventTLVValueDecoder.cpp b/src/controller/java/zap-generated/CHIPEventTLVValueDecoder.cpp index 89a4a256880158..b9d5e76c909135 100644 --- a/src/controller/java/zap-generated/CHIPEventTLVValueDecoder.cpp +++ b/src/controller/java/zap-generated/CHIPEventTLVValueDecoder.cpp @@ -5050,6 +5050,193 @@ jobject DecodeEventValue(const app::ConcreteEventPath & aPath, TLV::TLVReader & using namespace app::Clusters::WaterHeaterManagement; switch (aPath.mEventId) { + case Events::BoostStarted::Id: { + Events::BoostStarted::DecodableType cppValue; + *aError = app::DataModel::Decode(aReader, cppValue); + if (*aError != CHIP_NO_ERROR) + { + return nullptr; + } + jobject value_boostInfo; + jobject value_boostInfo_duration; + std::string value_boostInfo_durationClassName = "java/lang/Long"; + std::string value_boostInfo_durationCtorSignature = "(J)V"; + jlong jnivalue_boostInfo_duration = static_cast(cppValue.boostInfo.duration); + chip::JniReferences::GetInstance().CreateBoxedObject(value_boostInfo_durationClassName.c_str(), + value_boostInfo_durationCtorSignature.c_str(), + jnivalue_boostInfo_duration, value_boostInfo_duration); + jobject value_boostInfo_oneShot; + if (!cppValue.boostInfo.oneShot.HasValue()) + { + chip::JniReferences::GetInstance().CreateOptional(nullptr, value_boostInfo_oneShot); + } + else + { + jobject value_boostInfo_oneShotInsideOptional; + std::string value_boostInfo_oneShotInsideOptionalClassName = "java/lang/Boolean"; + std::string value_boostInfo_oneShotInsideOptionalCtorSignature = "(Z)V"; + jboolean jnivalue_boostInfo_oneShotInsideOptional = static_cast(cppValue.boostInfo.oneShot.Value()); + chip::JniReferences::GetInstance().CreateBoxedObject( + value_boostInfo_oneShotInsideOptionalClassName.c_str(), + value_boostInfo_oneShotInsideOptionalCtorSignature.c_str(), jnivalue_boostInfo_oneShotInsideOptional, + value_boostInfo_oneShotInsideOptional); + chip::JniReferences::GetInstance().CreateOptional(value_boostInfo_oneShotInsideOptional, value_boostInfo_oneShot); + } + jobject value_boostInfo_emergencyBoost; + if (!cppValue.boostInfo.emergencyBoost.HasValue()) + { + chip::JniReferences::GetInstance().CreateOptional(nullptr, value_boostInfo_emergencyBoost); + } + else + { + jobject value_boostInfo_emergencyBoostInsideOptional; + std::string value_boostInfo_emergencyBoostInsideOptionalClassName = "java/lang/Boolean"; + std::string value_boostInfo_emergencyBoostInsideOptionalCtorSignature = "(Z)V"; + jboolean jnivalue_boostInfo_emergencyBoostInsideOptional = + static_cast(cppValue.boostInfo.emergencyBoost.Value()); + chip::JniReferences::GetInstance().CreateBoxedObject( + value_boostInfo_emergencyBoostInsideOptionalClassName.c_str(), + value_boostInfo_emergencyBoostInsideOptionalCtorSignature.c_str(), + jnivalue_boostInfo_emergencyBoostInsideOptional, value_boostInfo_emergencyBoostInsideOptional); + chip::JniReferences::GetInstance().CreateOptional(value_boostInfo_emergencyBoostInsideOptional, + value_boostInfo_emergencyBoost); + } + jobject value_boostInfo_temporarySetpoint; + if (!cppValue.boostInfo.temporarySetpoint.HasValue()) + { + chip::JniReferences::GetInstance().CreateOptional(nullptr, value_boostInfo_temporarySetpoint); + } + else + { + jobject value_boostInfo_temporarySetpointInsideOptional; + std::string value_boostInfo_temporarySetpointInsideOptionalClassName = "java/lang/Integer"; + std::string value_boostInfo_temporarySetpointInsideOptionalCtorSignature = "(I)V"; + jint jnivalue_boostInfo_temporarySetpointInsideOptional = + static_cast(cppValue.boostInfo.temporarySetpoint.Value()); + chip::JniReferences::GetInstance().CreateBoxedObject( + value_boostInfo_temporarySetpointInsideOptionalClassName.c_str(), + value_boostInfo_temporarySetpointInsideOptionalCtorSignature.c_str(), + jnivalue_boostInfo_temporarySetpointInsideOptional, value_boostInfo_temporarySetpointInsideOptional); + chip::JniReferences::GetInstance().CreateOptional(value_boostInfo_temporarySetpointInsideOptional, + value_boostInfo_temporarySetpoint); + } + jobject value_boostInfo_targetPercentage; + if (!cppValue.boostInfo.targetPercentage.HasValue()) + { + chip::JniReferences::GetInstance().CreateOptional(nullptr, value_boostInfo_targetPercentage); + } + else + { + jobject value_boostInfo_targetPercentageInsideOptional; + std::string value_boostInfo_targetPercentageInsideOptionalClassName = "java/lang/Integer"; + std::string value_boostInfo_targetPercentageInsideOptionalCtorSignature = "(I)V"; + jint jnivalue_boostInfo_targetPercentageInsideOptional = + static_cast(cppValue.boostInfo.targetPercentage.Value()); + chip::JniReferences::GetInstance().CreateBoxedObject( + value_boostInfo_targetPercentageInsideOptionalClassName.c_str(), + value_boostInfo_targetPercentageInsideOptionalCtorSignature.c_str(), + jnivalue_boostInfo_targetPercentageInsideOptional, value_boostInfo_targetPercentageInsideOptional); + chip::JniReferences::GetInstance().CreateOptional(value_boostInfo_targetPercentageInsideOptional, + value_boostInfo_targetPercentage); + } + jobject value_boostInfo_targetReheat; + if (!cppValue.boostInfo.targetReheat.HasValue()) + { + chip::JniReferences::GetInstance().CreateOptional(nullptr, value_boostInfo_targetReheat); + } + else + { + jobject value_boostInfo_targetReheatInsideOptional; + std::string value_boostInfo_targetReheatInsideOptionalClassName = "java/lang/Integer"; + std::string value_boostInfo_targetReheatInsideOptionalCtorSignature = "(I)V"; + jint jnivalue_boostInfo_targetReheatInsideOptional = static_cast(cppValue.boostInfo.targetReheat.Value()); + chip::JniReferences::GetInstance().CreateBoxedObject( + value_boostInfo_targetReheatInsideOptionalClassName.c_str(), + value_boostInfo_targetReheatInsideOptionalCtorSignature.c_str(), jnivalue_boostInfo_targetReheatInsideOptional, + value_boostInfo_targetReheatInsideOptional); + chip::JniReferences::GetInstance().CreateOptional(value_boostInfo_targetReheatInsideOptional, + value_boostInfo_targetReheat); + } + + jclass waterHeaterBoostInfoStructStructClass_0; + err = chip::JniReferences::GetInstance().GetLocalClassRef( + env, "chip/devicecontroller/ChipStructs$WaterHeaterManagementClusterWaterHeaterBoostInfoStruct", + waterHeaterBoostInfoStructStructClass_0); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Could not find class ChipStructs$WaterHeaterManagementClusterWaterHeaterBoostInfoStruct"); + return nullptr; + } + + jmethodID waterHeaterBoostInfoStructStructCtor_0; + err = chip::JniReferences::GetInstance().FindMethod(env, waterHeaterBoostInfoStructStructClass_0, "", + "(Ljava/lang/Long;Ljava/util/Optional;Ljava/util/Optional;Ljava/" + "util/Optional;Ljava/util/Optional;Ljava/util/Optional;)V", + &waterHeaterBoostInfoStructStructCtor_0); + if (err != CHIP_NO_ERROR || waterHeaterBoostInfoStructStructCtor_0 == nullptr) + { + ChipLogError(Zcl, "Could not find ChipStructs$WaterHeaterManagementClusterWaterHeaterBoostInfoStruct constructor"); + return nullptr; + } + + value_boostInfo = + env->NewObject(waterHeaterBoostInfoStructStructClass_0, waterHeaterBoostInfoStructStructCtor_0, + value_boostInfo_duration, value_boostInfo_oneShot, value_boostInfo_emergencyBoost, + value_boostInfo_temporarySetpoint, value_boostInfo_targetPercentage, value_boostInfo_targetReheat); + + jclass boostStartedStructClass; + err = chip::JniReferences::GetInstance().GetLocalClassRef( + env, "chip/devicecontroller/ChipEventStructs$WaterHeaterManagementClusterBoostStartedEvent", + boostStartedStructClass); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Could not find class ChipEventStructs$WaterHeaterManagementClusterBoostStartedEvent"); + return nullptr; + } + + jmethodID boostStartedStructCtor; + err = chip::JniReferences::GetInstance().FindMethod( + env, boostStartedStructClass, "", + "(Lchip/devicecontroller/ChipStructs$WaterHeaterManagementClusterWaterHeaterBoostInfoStruct;)V", + &boostStartedStructCtor); + if (err != CHIP_NO_ERROR || boostStartedStructCtor == nullptr) + { + ChipLogError(Zcl, "Could not find ChipEventStructs$WaterHeaterManagementClusterBoostStartedEvent constructor"); + return nullptr; + } + + jobject value = env->NewObject(boostStartedStructClass, boostStartedStructCtor, value_boostInfo); + + return value; + } + case Events::BoostEnded::Id: { + Events::BoostEnded::DecodableType cppValue; + *aError = app::DataModel::Decode(aReader, cppValue); + if (*aError != CHIP_NO_ERROR) + { + return nullptr; + } + jclass boostEndedStructClass; + err = chip::JniReferences::GetInstance().GetLocalClassRef( + env, "chip/devicecontroller/ChipEventStructs$WaterHeaterManagementClusterBoostEndedEvent", boostEndedStructClass); + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Could not find class ChipEventStructs$WaterHeaterManagementClusterBoostEndedEvent"); + return nullptr; + } + + jmethodID boostEndedStructCtor; + err = chip::JniReferences::GetInstance().FindMethod(env, boostEndedStructClass, "", "()V", &boostEndedStructCtor); + if (err != CHIP_NO_ERROR || boostEndedStructCtor == nullptr) + { + ChipLogError(Zcl, "Could not find ChipEventStructs$WaterHeaterManagementClusterBoostEndedEvent constructor"); + return nullptr; + } + + jobject value = env->NewObject(boostEndedStructClass, boostEndedStructCtor); + + return value; + } default: *aError = CHIP_ERROR_IM_MALFORMED_EVENT_PATH_IB; break; diff --git a/src/controller/python/ChipDeviceController-ScriptBinding.cpp b/src/controller/python/ChipDeviceController-ScriptBinding.cpp index d9b557bb62be67..b31cb5b7b8a4e6 100644 --- a/src/controller/python/ChipDeviceController-ScriptBinding.cpp +++ b/src/controller/python/ChipDeviceController-ScriptBinding.cpp @@ -839,7 +839,11 @@ PyChipError pychip_IsSessionOverTCPConnection(chip::OperationalDeviceProxy * dev VerifyOrReturnError(deviceProxy->GetSecureSession().HasValue(), ToPyChipError(CHIP_ERROR_MISSING_SECURE_SESSION)); VerifyOrReturnError(isSessionOverTCP != nullptr, ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT)); +#if INET_CONFIG_ENABLE_TCP_ENDPOINT *isSessionOverTCP = deviceProxy->GetSecureSession().Value()->AsSecureSession()->GetTCPConnection() != nullptr; +#else + *isSessionOverTCP = false; +#endif return ToPyChipError(CHIP_NO_ERROR); } @@ -859,6 +863,7 @@ PyChipError pychip_IsActiveSession(chip::OperationalDeviceProxy * deviceProxy, b PyChipError pychip_CloseTCPConnectionWithPeer(chip::OperationalDeviceProxy * deviceProxy) { +#if INET_CONFIG_ENABLE_TCP_ENDPOINT VerifyOrReturnError(deviceProxy->GetSecureSession().HasValue(), ToPyChipError(CHIP_ERROR_MISSING_SECURE_SESSION)); VerifyOrReturnError(deviceProxy->GetSecureSession().Value()->AsSecureSession()->AllowsLargePayload(), ToPyChipError(CHIP_ERROR_INVALID_ARGUMENT)); @@ -867,6 +872,9 @@ PyChipError pychip_CloseTCPConnectionWithPeer(chip::OperationalDeviceProxy * dev deviceProxy->GetSecureSession().Value()->AsSecureSession()->GetTCPConnection(), /* shouldAbort = */ false); return ToPyChipError(CHIP_NO_ERROR); +#else + return ToPyChipError(CHIP_ERROR_NOT_IMPLEMENTED); +#endif } PyChipError pychip_FreeOperationalDeviceProxy(chip::OperationalDeviceProxy * deviceProxy) diff --git a/src/controller/python/chip/clusters/CHIPClusters.py b/src/controller/python/chip/clusters/CHIPClusters.py index c573d69dc78e6c..028810afd4eb77 100644 --- a/src/controller/python/chip/clusters/CHIPClusters.py +++ b/src/controller/python/chip/clusters/CHIPClusters.py @@ -6658,12 +6658,7 @@ class ChipClusters: "commandId": 0x00000000, "commandName": "Boost", "args": { - "duration": "int", - "oneShot": "bool", - "emergencyBoost": "bool", - "temporarySetpoint": "int", - "targetPercentage": "int", - "targetReheat": "int", + "boostInfo": "WaterHeaterBoostInfoStruct", }, }, 0x00000001: { diff --git a/src/controller/python/chip/clusters/Objects.py b/src/controller/python/chip/clusters/Objects.py index 405d8b8872bcba..cff7adf4ae6fbb 100644 --- a/src/controller/python/chip/clusters/Objects.py +++ b/src/controller/python/chip/clusters/Objects.py @@ -23909,28 +23909,16 @@ class Feature(IntFlag): kEnergyManagement = 0x1 kTankPercent = 0x2 - class WaterHeaterDemandBitmap(IntFlag): + class WaterHeaterHeatSourceBitmap(IntFlag): kImmersionElement1 = 0x1 kImmersionElement2 = 0x2 kHeatPump = 0x4 kBoiler = 0x8 kOther = 0x10 - class WaterHeaterTypeBitmap(IntFlag): - kImmersionElement1 = 0x1 - kImmersionElement2 = 0x2 - kHeatPump = 0x4 - kBoiler = 0x8 - kOther = 0x10 - - class Commands: + class Structs: @dataclass - class Boost(ClusterCommand): - cluster_id: typing.ClassVar[int] = 0x00000094 - command_id: typing.ClassVar[int] = 0x00000000 - is_client: typing.ClassVar[bool] = True - response_type: typing.ClassVar[str] = None - + class WaterHeaterBoostInfoStruct(ClusterObject): @ChipUtility.classproperty def descriptor(cls) -> ClusterObjectDescriptor: return ClusterObjectDescriptor( @@ -23950,6 +23938,23 @@ def descriptor(cls) -> ClusterObjectDescriptor: targetPercentage: 'typing.Optional[uint]' = None targetReheat: 'typing.Optional[uint]' = None + class Commands: + @dataclass + class Boost(ClusterCommand): + cluster_id: typing.ClassVar[int] = 0x00000094 + command_id: typing.ClassVar[int] = 0x00000000 + is_client: typing.ClassVar[bool] = True + response_type: typing.ClassVar[str] = None + + @ChipUtility.classproperty + def descriptor(cls) -> ClusterObjectDescriptor: + return ClusterObjectDescriptor( + Fields=[ + ClusterObjectFieldDescriptor(Label="boostInfo", Tag=0, Type=WaterHeaterManagement.Structs.WaterHeaterBoostInfoStruct), + ]) + + boostInfo: 'WaterHeaterManagement.Structs.WaterHeaterBoostInfoStruct' = field(default_factory=lambda: WaterHeaterManagement.Structs.WaterHeaterBoostInfoStruct()) + @dataclass class CancelBoost(ClusterCommand): cluster_id: typing.ClassVar[int] = 0x00000094 @@ -24156,6 +24161,42 @@ def attribute_type(cls) -> ClusterObjectFieldDescriptor: value: 'uint' = 0 + class Events: + @dataclass + class BoostStarted(ClusterEvent): + @ChipUtility.classproperty + def cluster_id(cls) -> int: + return 0x00000094 + + @ChipUtility.classproperty + def event_id(cls) -> int: + return 0x00000000 + + @ChipUtility.classproperty + def descriptor(cls) -> ClusterObjectDescriptor: + return ClusterObjectDescriptor( + Fields=[ + ClusterObjectFieldDescriptor(Label="boostInfo", Tag=0, Type=WaterHeaterManagement.Structs.WaterHeaterBoostInfoStruct), + ]) + + boostInfo: 'WaterHeaterManagement.Structs.WaterHeaterBoostInfoStruct' = field(default_factory=lambda: WaterHeaterManagement.Structs.WaterHeaterBoostInfoStruct()) + + @dataclass + class BoostEnded(ClusterEvent): + @ChipUtility.classproperty + def cluster_id(cls) -> int: + return 0x00000094 + + @ChipUtility.classproperty + def event_id(cls) -> int: + return 0x00000001 + + @ChipUtility.classproperty + def descriptor(cls) -> ClusterObjectDescriptor: + return ClusterObjectDescriptor( + Fields=[ + ]) + @dataclass class DemandResponseLoadControl(Cluster): diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h index 9e864b458de94d..fab6862dd96a67 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRBaseClusters.h @@ -18913,20 +18913,12 @@ typedef NS_OPTIONS(uint32_t, MTRWaterHeaterManagementFeature) { MTRWaterHeaterManagementFeatureTankPercent MTR_PROVISIONALLY_AVAILABLE = 0x2, } MTR_PROVISIONALLY_AVAILABLE; -typedef NS_OPTIONS(uint8_t, MTRWaterHeaterManagementWaterHeaterDemandBitmap) { - MTRWaterHeaterManagementWaterHeaterDemandBitmapImmersionElement1 MTR_PROVISIONALLY_AVAILABLE = 0x1, - MTRWaterHeaterManagementWaterHeaterDemandBitmapImmersionElement2 MTR_PROVISIONALLY_AVAILABLE = 0x2, - MTRWaterHeaterManagementWaterHeaterDemandBitmapHeatPump MTR_PROVISIONALLY_AVAILABLE = 0x4, - MTRWaterHeaterManagementWaterHeaterDemandBitmapBoiler MTR_PROVISIONALLY_AVAILABLE = 0x8, - MTRWaterHeaterManagementWaterHeaterDemandBitmapOther MTR_PROVISIONALLY_AVAILABLE = 0x10, -} MTR_PROVISIONALLY_AVAILABLE; - -typedef NS_OPTIONS(uint8_t, MTRWaterHeaterManagementWaterHeaterTypeBitmap) { - MTRWaterHeaterManagementWaterHeaterTypeBitmapImmersionElement1 MTR_PROVISIONALLY_AVAILABLE = 0x1, - MTRWaterHeaterManagementWaterHeaterTypeBitmapImmersionElement2 MTR_PROVISIONALLY_AVAILABLE = 0x2, - MTRWaterHeaterManagementWaterHeaterTypeBitmapHeatPump MTR_PROVISIONALLY_AVAILABLE = 0x4, - MTRWaterHeaterManagementWaterHeaterTypeBitmapBoiler MTR_PROVISIONALLY_AVAILABLE = 0x8, - MTRWaterHeaterManagementWaterHeaterTypeBitmapOther MTR_PROVISIONALLY_AVAILABLE = 0x10, +typedef NS_OPTIONS(uint8_t, MTRWaterHeaterManagementWaterHeaterHeatSourceBitmap) { + MTRWaterHeaterManagementWaterHeaterHeatSourceBitmapImmersionElement1 MTR_PROVISIONALLY_AVAILABLE = 0x1, + MTRWaterHeaterManagementWaterHeaterHeatSourceBitmapImmersionElement2 MTR_PROVISIONALLY_AVAILABLE = 0x2, + MTRWaterHeaterManagementWaterHeaterHeatSourceBitmapHeatPump MTR_PROVISIONALLY_AVAILABLE = 0x4, + MTRWaterHeaterManagementWaterHeaterHeatSourceBitmapBoiler MTR_PROVISIONALLY_AVAILABLE = 0x8, + MTRWaterHeaterManagementWaterHeaterHeatSourceBitmapOther MTR_PROVISIONALLY_AVAILABLE = 0x10, } MTR_PROVISIONALLY_AVAILABLE; typedef NS_ENUM(uint8_t, MTRDemandResponseLoadControlCriticalityLevel) { diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRClusterConstants.h b/src/darwin/Framework/CHIP/zap-generated/MTRClusterConstants.h index 2e5c8cc7dc1b44..33251439bbc237 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRClusterConstants.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRClusterConstants.h @@ -7372,6 +7372,10 @@ typedef NS_ENUM(uint32_t, MTREventIDType) { MTREventIDTypeClusterElectricalEnergyMeasurementEventCumulativeEnergyMeasuredID MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6)) = 0x00000000, MTREventIDTypeClusterElectricalEnergyMeasurementEventPeriodicEnergyMeasuredID MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6)) = 0x00000001, + // Cluster WaterHeaterManagement events + MTREventIDTypeClusterWaterHeaterManagementEventBoostStartedID MTR_PROVISIONALLY_AVAILABLE = 0x00000000, + MTREventIDTypeClusterWaterHeaterManagementEventBoostEndedID MTR_PROVISIONALLY_AVAILABLE = 0x00000001, + // Cluster DemandResponseLoadControl events MTREventIDTypeClusterDemandResponseLoadControlEventLoadControlEventStatusChangeID MTR_PROVISIONALLY_AVAILABLE = 0x00000000, diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRClusterNames.mm b/src/darwin/Framework/CHIP/zap-generated/MTRClusterNames.mm index fa08192303b8fd..d3715b49d0d250 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRClusterNames.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRClusterNames.mm @@ -10355,6 +10355,15 @@ switch (eventID) { + // Cluster WaterHeaterManagement events + case MTREventIDTypeClusterWaterHeaterManagementEventBoostStartedID: + result = @"BoostStarted"; + break; + + case MTREventIDTypeClusterWaterHeaterManagementEventBoostEndedID: + result = @"BoostEnded"; + break; + default: result = [NSString stringWithFormat:@"", eventID]; break; diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h index b276bb8b1986ac..5c869616268ecc 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.h @@ -5495,17 +5495,7 @@ MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6)) MTR_PROVISIONALLY_AVAILABLE @interface MTRWaterHeaterManagementClusterBoostParams : NSObject -@property (nonatomic, copy) NSNumber * _Nonnull duration MTR_PROVISIONALLY_AVAILABLE; - -@property (nonatomic, copy) NSNumber * _Nullable oneShot MTR_PROVISIONALLY_AVAILABLE; - -@property (nonatomic, copy) NSNumber * _Nullable emergencyBoost MTR_PROVISIONALLY_AVAILABLE; - -@property (nonatomic, copy) NSNumber * _Nullable temporarySetpoint MTR_PROVISIONALLY_AVAILABLE; - -@property (nonatomic, copy) NSNumber * _Nullable targetPercentage MTR_PROVISIONALLY_AVAILABLE; - -@property (nonatomic, copy) NSNumber * _Nullable targetReheat MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) MTRWaterHeaterManagementClusterWaterHeaterBoostInfoStruct * _Nonnull boostInfo MTR_PROVISIONALLY_AVAILABLE; /** * Controls whether the command is a timed command (using Timed Invoke). * diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm index 7aa736277256f6..976d5a0ef241be 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRCommandPayloadsObjc.mm @@ -15466,17 +15466,7 @@ - (instancetype)init { if (self = [super init]) { - _duration = @(0); - - _oneShot = nil; - - _emergencyBoost = nil; - - _temporarySetpoint = nil; - - _targetPercentage = nil; - - _targetReheat = nil; + _boostInfo = [MTRWaterHeaterManagementClusterWaterHeaterBoostInfoStruct new]; _timedInvokeTimeoutMs = nil; _serverSideProcessingTimeout = nil; } @@ -15487,12 +15477,7 @@ - (id)copyWithZone:(NSZone * _Nullable)zone; { auto other = [[MTRWaterHeaterManagementClusterBoostParams alloc] init]; - other.duration = self.duration; - other.oneShot = self.oneShot; - other.emergencyBoost = self.emergencyBoost; - other.temporarySetpoint = self.temporarySetpoint; - other.targetPercentage = self.targetPercentage; - other.targetReheat = self.targetReheat; + other.boostInfo = self.boostInfo; other.timedInvokeTimeoutMs = self.timedInvokeTimeoutMs; other.serverSideProcessingTimeout = self.serverSideProcessingTimeout; @@ -15501,7 +15486,7 @@ - (id)copyWithZone:(NSZone * _Nullable)zone; - (NSString *)description { - NSString * descriptionString = [NSString stringWithFormat:@"<%@: duration:%@; oneShot:%@; emergencyBoost:%@; temporarySetpoint:%@; targetPercentage:%@; targetReheat:%@; >", NSStringFromClass([self class]), _duration, _oneShot, _emergencyBoost, _temporarySetpoint, _targetPercentage, _targetReheat]; + NSString * descriptionString = [NSString stringWithFormat:@"<%@: boostInfo:%@; >", NSStringFromClass([self class]), _boostInfo]; return descriptionString; } @@ -15514,36 +15499,26 @@ - (CHIP_ERROR)_encodeToTLVReader:(chip::System::PacketBufferTLVReader &)reader chip::app::Clusters::WaterHeaterManagement::Commands::Boost::Type encodableStruct; ListFreer listFreer; { - encodableStruct.duration = self.duration.unsignedIntValue; - } - { - if (self.oneShot != nil) { - auto & definedValue_0 = encodableStruct.oneShot.Emplace(); - definedValue_0 = self.oneShot.boolValue; + encodableStruct.boostInfo.duration = self.boostInfo.duration.unsignedIntValue; + if (self.boostInfo.oneShot != nil) { + auto & definedValue_1 = encodableStruct.boostInfo.oneShot.Emplace(); + definedValue_1 = self.boostInfo.oneShot.boolValue; } - } - { - if (self.emergencyBoost != nil) { - auto & definedValue_0 = encodableStruct.emergencyBoost.Emplace(); - definedValue_0 = self.emergencyBoost.boolValue; + if (self.boostInfo.emergencyBoost != nil) { + auto & definedValue_1 = encodableStruct.boostInfo.emergencyBoost.Emplace(); + definedValue_1 = self.boostInfo.emergencyBoost.boolValue; } - } - { - if (self.temporarySetpoint != nil) { - auto & definedValue_0 = encodableStruct.temporarySetpoint.Emplace(); - definedValue_0 = self.temporarySetpoint.shortValue; + if (self.boostInfo.temporarySetpoint != nil) { + auto & definedValue_1 = encodableStruct.boostInfo.temporarySetpoint.Emplace(); + definedValue_1 = self.boostInfo.temporarySetpoint.shortValue; } - } - { - if (self.targetPercentage != nil) { - auto & definedValue_0 = encodableStruct.targetPercentage.Emplace(); - definedValue_0 = self.targetPercentage.unsignedCharValue; + if (self.boostInfo.targetPercentage != nil) { + auto & definedValue_1 = encodableStruct.boostInfo.targetPercentage.Emplace(); + definedValue_1 = self.boostInfo.targetPercentage.unsignedCharValue; } - } - { - if (self.targetReheat != nil) { - auto & definedValue_0 = encodableStruct.targetReheat.Emplace(); - definedValue_0 = self.targetReheat.unsignedCharValue; + if (self.boostInfo.targetReheat != nil) { + auto & definedValue_1 = encodableStruct.boostInfo.targetReheat.Emplace(); + definedValue_1 = self.boostInfo.targetReheat.unsignedCharValue; } } diff --git a/src/darwin/Framework/CHIP/zap-generated/MTREventTLVValueDecoder.mm b/src/darwin/Framework/CHIP/zap-generated/MTREventTLVValueDecoder.mm index 821b8f6d58173d..cb2e1800de750f 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTREventTLVValueDecoder.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTREventTLVValueDecoder.mm @@ -2807,6 +2807,60 @@ static id _Nullable DecodeEventPayloadForWaterHeaterManagementCluster(EventId aE { using namespace Clusters::WaterHeaterManagement; switch (aEventId) { + case Events::BoostStarted::Id: { + Events::BoostStarted::DecodableType cppValue; + *aError = DataModel::Decode(aReader, cppValue); + if (*aError != CHIP_NO_ERROR) { + return nil; + } + + __auto_type * value = [MTRWaterHeaterManagementClusterBoostStartedEvent new]; + + do { + MTRWaterHeaterManagementClusterWaterHeaterBoostInfoStruct * _Nonnull memberValue; + memberValue = [MTRWaterHeaterManagementClusterWaterHeaterBoostInfoStruct new]; + memberValue.duration = [NSNumber numberWithUnsignedInt:cppValue.boostInfo.duration]; + if (cppValue.boostInfo.oneShot.HasValue()) { + memberValue.oneShot = [NSNumber numberWithBool:cppValue.boostInfo.oneShot.Value()]; + } else { + memberValue.oneShot = nil; + } + if (cppValue.boostInfo.emergencyBoost.HasValue()) { + memberValue.emergencyBoost = [NSNumber numberWithBool:cppValue.boostInfo.emergencyBoost.Value()]; + } else { + memberValue.emergencyBoost = nil; + } + if (cppValue.boostInfo.temporarySetpoint.HasValue()) { + memberValue.temporarySetpoint = [NSNumber numberWithShort:cppValue.boostInfo.temporarySetpoint.Value()]; + } else { + memberValue.temporarySetpoint = nil; + } + if (cppValue.boostInfo.targetPercentage.HasValue()) { + memberValue.targetPercentage = [NSNumber numberWithUnsignedChar:cppValue.boostInfo.targetPercentage.Value()]; + } else { + memberValue.targetPercentage = nil; + } + if (cppValue.boostInfo.targetReheat.HasValue()) { + memberValue.targetReheat = [NSNumber numberWithUnsignedChar:cppValue.boostInfo.targetReheat.Value()]; + } else { + memberValue.targetReheat = nil; + } + value.boostInfo = memberValue; + } while (0); + + return value; + } + case Events::BoostEnded::Id: { + Events::BoostEnded::DecodableType cppValue; + *aError = DataModel::Decode(aReader, cppValue); + if (*aError != CHIP_NO_ERROR) { + return nil; + } + + __auto_type * value = [MTRWaterHeaterManagementClusterBoostEndedEvent new]; + + return value; + } default: { break; } diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h index 8590079420a854..35df5cc3e32495 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h +++ b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.h @@ -1235,6 +1235,25 @@ MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6)) @property (nonatomic, copy) MTRElectricalEnergyMeasurementClusterEnergyMeasurementStruct * _Nullable energyExported MTR_AVAILABLE(ios(17.6), macos(14.6), watchos(10.6), tvos(17.6)); @end +MTR_PROVISIONALLY_AVAILABLE +@interface MTRWaterHeaterManagementClusterWaterHeaterBoostInfoStruct : NSObject +@property (nonatomic, copy) NSNumber * _Nonnull duration MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) NSNumber * _Nullable oneShot MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) NSNumber * _Nullable emergencyBoost MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) NSNumber * _Nullable temporarySetpoint MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) NSNumber * _Nullable targetPercentage MTR_PROVISIONALLY_AVAILABLE; +@property (nonatomic, copy) NSNumber * _Nullable targetReheat MTR_PROVISIONALLY_AVAILABLE; +@end + +MTR_PROVISIONALLY_AVAILABLE +@interface MTRWaterHeaterManagementClusterBoostStartedEvent : NSObject +@property (nonatomic, copy) MTRWaterHeaterManagementClusterWaterHeaterBoostInfoStruct * _Nonnull boostInfo MTR_PROVISIONALLY_AVAILABLE; +@end + +MTR_PROVISIONALLY_AVAILABLE +@interface MTRWaterHeaterManagementClusterBoostEndedEvent : NSObject +@end + MTR_PROVISIONALLY_AVAILABLE @interface MTRDemandResponseLoadControlClusterHeatingSourceControlStruct : NSObject @property (nonatomic, copy) NSNumber * _Nonnull heatingSource MTR_PROVISIONALLY_AVAILABLE; diff --git a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm index 02aac9e24ec0ae..6c3fc19e9b68bf 100644 --- a/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm +++ b/src/darwin/Framework/CHIP/zap-generated/MTRStructsObjc.mm @@ -5029,6 +5029,98 @@ - (NSString *)description @end +@implementation MTRWaterHeaterManagementClusterWaterHeaterBoostInfoStruct +- (instancetype)init +{ + if (self = [super init]) { + + _duration = @(0); + + _oneShot = nil; + + _emergencyBoost = nil; + + _temporarySetpoint = nil; + + _targetPercentage = nil; + + _targetReheat = nil; + } + return self; +} + +- (id)copyWithZone:(NSZone * _Nullable)zone +{ + auto other = [[MTRWaterHeaterManagementClusterWaterHeaterBoostInfoStruct alloc] init]; + + other.duration = self.duration; + other.oneShot = self.oneShot; + other.emergencyBoost = self.emergencyBoost; + other.temporarySetpoint = self.temporarySetpoint; + other.targetPercentage = self.targetPercentage; + other.targetReheat = self.targetReheat; + + return other; +} + +- (NSString *)description +{ + NSString * descriptionString = [NSString stringWithFormat:@"<%@: duration:%@; oneShot:%@; emergencyBoost:%@; temporarySetpoint:%@; targetPercentage:%@; targetReheat:%@; >", NSStringFromClass([self class]), _duration, _oneShot, _emergencyBoost, _temporarySetpoint, _targetPercentage, _targetReheat]; + return descriptionString; +} + +@end + +@implementation MTRWaterHeaterManagementClusterBoostStartedEvent +- (instancetype)init +{ + if (self = [super init]) { + + _boostInfo = [MTRWaterHeaterManagementClusterWaterHeaterBoostInfoStruct new]; + } + return self; +} + +- (id)copyWithZone:(NSZone * _Nullable)zone +{ + auto other = [[MTRWaterHeaterManagementClusterBoostStartedEvent alloc] init]; + + other.boostInfo = self.boostInfo; + + return other; +} + +- (NSString *)description +{ + NSString * descriptionString = [NSString stringWithFormat:@"<%@: boostInfo:%@; >", NSStringFromClass([self class]), _boostInfo]; + return descriptionString; +} + +@end + +@implementation MTRWaterHeaterManagementClusterBoostEndedEvent +- (instancetype)init +{ + if (self = [super init]) { + } + return self; +} + +- (id)copyWithZone:(NSZone * _Nullable)zone +{ + auto other = [[MTRWaterHeaterManagementClusterBoostEndedEvent alloc] init]; + + return other; +} + +- (NSString *)description +{ + NSString * descriptionString = [NSString stringWithFormat:@"<%@: >", NSStringFromClass([self class])]; + return descriptionString; +} + +@end + @implementation MTRDemandResponseLoadControlClusterHeatingSourceControlStruct - (instancetype)init { diff --git a/src/lib/support/ThreadOperationalDataset.cpp b/src/lib/support/ThreadOperationalDataset.cpp index 1c11712166cfa4..39ba6b3b33b196 100644 --- a/src/lib/support/ThreadOperationalDataset.cpp +++ b/src/lib/support/ThreadOperationalDataset.cpp @@ -55,6 +55,7 @@ class ThreadTLV final kMeshLocalPrefix = 7, kSecurityPolicy = 12, kActiveTimestamp = 14, + kDelayTimer = 52, kChannelMask = 53, }; @@ -426,6 +427,24 @@ CHIP_ERROR OperationalDataset::SetSecurityPolicy(uint32_t aSecurityPolicy) return CHIP_NO_ERROR; } +CHIP_ERROR OperationalDataset::GetDelayTimer(uint32_t & aDelayMillis) const +{ + const ThreadTLV * tlv = Locate(ThreadTLV::kDelayTimer); + VerifyOrReturnError(tlv != nullptr, CHIP_ERROR_TLV_TAG_NOT_FOUND); + VerifyOrReturnError(tlv->GetLength() == sizeof(aDelayMillis), CHIP_ERROR_INVALID_TLV_ELEMENT); + tlv->Get32(aDelayMillis); + return CHIP_NO_ERROR; +} + +CHIP_ERROR OperationalDataset::SetDelayTimer(uint32_t aDelayMillis) +{ + ThreadTLV * tlv = MakeRoom(ThreadTLV::kDelayTimer, sizeof(*tlv) + sizeof(aDelayMillis)); + VerifyOrReturnError(tlv != nullptr, CHIP_ERROR_NO_MEMORY); + tlv->Set32(aDelayMillis); + mLength = static_cast(mLength + tlv->GetSize()); + return CHIP_NO_ERROR; +} + void OperationalDataset::UnsetMasterKey() { Remove(ThreadTLV::kMasterKey); diff --git a/src/lib/support/ThreadOperationalDataset.h b/src/lib/support/ThreadOperationalDataset.h index 5cd57df6a79db6..a19d9827bbd610 100644 --- a/src/lib/support/ThreadOperationalDataset.h +++ b/src/lib/support/ThreadOperationalDataset.h @@ -294,6 +294,23 @@ class OperationalDataset */ CHIP_ERROR SetSecurityPolicy(uint32_t aSecurityPolicy); + /** + * Retrieves the delay timer from the dataset. + * + * @retval CHIP_NO_ERROR on success. + * @retval CHIP_ERROR_TLV_TAG_NOT_FOUND if no security policy is present in the dataset. + * @retval CHIP_ERROR_INVALID_TLV_ELEMENT if the TLV element is invalid. + */ + CHIP_ERROR GetDelayTimer(uint32_t & aDelayMillis) const; + + /** + * This method sets the delay timer within the dataset. + * + * @retval CHIP_NO_ERROR on success. + * @retval CHIP_ERROR_NO_MEMORY if there is insufficient space within the dataset. + */ + CHIP_ERROR SetDelayTimer(uint32_t aDelayMillis); + /** * This method clears all data stored in the dataset. */ diff --git a/src/python_testing/TC_EWATERHTRBase.py b/src/python_testing/TC_EWATERHTRBase.py index 0bf42a7eb4d0a0..6aba67edd20140 100644 --- a/src/python_testing/TC_EWATERHTRBase.py +++ b/src/python_testing/TC_EWATERHTRBase.py @@ -42,15 +42,16 @@ async def send_boost_command(self, duration: int, one_shot: typing.Optional[bool endpoint: int = None, timedRequestTimeoutMs: int = 3000, expected_status: Status = Status.Success): try: - await self.send_single_cmd(cmd=Clusters.WaterHeaterManagement.Commands.Boost( - duration=duration, - oneShot=one_shot, - emergencyBoost=emergency_boost, - temporarySetpoint=temporary_setpoint, - targetPercentage=target_percentage, - targetReheat=target_reheat), - endpoint=endpoint, - timedRequestTimeoutMs=timedRequestTimeoutMs) + boostInfo = Clusters.WaterHeaterManagement.Structs.WaterHeaterBoostInfoStruct(duration=duration, + oneShot=one_shot, + emergencyBoost=emergency_boost, + temporarySetpoint=temporary_setpoint, + targetPercentage=target_percentage, + targetReheat=target_reheat) + + await self.send_single_cmd(cmd=Clusters.WaterHeaterManagement.Commands.Boost(boostInfo=boostInfo), + endpoint=endpoint, + timedRequestTimeoutMs=timedRequestTimeoutMs) asserts.assert_equal(expected_status, Status.Success) diff --git a/src/python_testing/TC_EWATERHTR_2_1.py b/src/python_testing/TC_EWATERHTR_2_1.py index c50a082ae2afa1..176ecf2f95a173 100644 --- a/src/python_testing/TC_EWATERHTR_2_1.py +++ b/src/python_testing/TC_EWATERHTR_2_1.py @@ -23,7 +23,7 @@ # test-runner-run/run1/app: ${ALL_CLUSTERS_APP} # test-runner-run/run1/factoryreset: True # test-runner-run/run1/quiet: True -# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json --enable-key 000102030405060708090a0b0c0d0e0f --featureSet 0x03 +# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json --enable-key 000102030405060708090a0b0c0d0e0f # test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --hex-arg enableKey:000102030405060708090a0b0c0d0e0f --endpoint 1 --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto --int-arg PIXIT.EWATERHTR.EM:1 PIXIT.EWATERHTR.TP:2 # === END CI TEST ARGUMENTS === @@ -53,9 +53,9 @@ def steps_TC_EWATERHTR_2_1(self) -> list[TestStep]: TestStep("1", "Commissioning, already done", is_commissioning=True), TestStep("2", "TH reads HeaterTypes attribute.", - "DUT as Server replies with a WaterHeaterTypeBitmap (enum8) greater than 0x00 (at least one type supported), and less than 0x20 (no undefined types supported)."), + "DUT as Server replies with a WaterHeaterHeatSourceBitmap (enum8) greater than 0x00 (at least one type supported), and less than 0x20 (no undefined types supported)."), TestStep("3", "TH reads HeatDemand attribute.", - "DUT as Server replies with a WaterHeaterDemandBitmap (enum8)."), + "DUT as Server replies with a WaterHeaterHeatSourceBitmap (enum8)."), TestStep("4", "TH reads TankVolume attribute.", "DUT as Server replies with a uint16 value."), TestStep("5", "TH reads EstimatedHeatRequired attribute.", @@ -81,15 +81,15 @@ async def test_TC_EWATERHTR_2_1(self): heaterTypes = await self.read_whm_attribute_expect_success(attribute="HeaterTypes") asserts.assert_greater(heaterTypes, 0, f"Unexpected HeaterTypes value - expected {heaterTypes} > 0") - asserts.assert_less_equal(heaterTypes, Clusters.WaterHeaterManagement.Bitmaps.WaterHeaterTypeBitmap.kOther, - f"Unexpected HeaterTypes value - expected {heaterTypes} <= WaterHeaterTypeBitmap.kOther") + asserts.assert_less_equal(heaterTypes, Clusters.WaterHeaterManagement.Bitmaps.WaterHeaterHeatSourceBitmap.kOther, + f"Unexpected HeaterTypes value - expected {heaterTypes} <= WaterHeaterHeatSourceBitmap.kOther") self.step("3") heatDemand = await self.read_whm_attribute_expect_success(attribute="HeatDemand") asserts.assert_greater(heatDemand, 0, f"Unexpected HeatDemand value - expected {heatDemand} > 0") - asserts.assert_less_equal(heatDemand, Clusters.WaterHeaterManagement.Bitmaps.WaterHeaterDemandBitmap.kOther, - f"Unexpected HeatDemand value - expected {heatDemand} <= WaterHeaterDemandBitmap.kOther") + asserts.assert_less_equal(heatDemand, Clusters.WaterHeaterManagement.Bitmaps.WaterHeaterHeatSourceBitmap.kOther, + f"Unexpected HeatDemand value - expected {heatDemand} <= WaterHeaterHeatSourceBitmap.kOther") self.step("4") if em_supported: diff --git a/src/python_testing/TC_EWATERHTR_2_2.py b/src/python_testing/TC_EWATERHTR_2_2.py index 7ce8c815a63a3b..9eafd35afaed33 100644 --- a/src/python_testing/TC_EWATERHTR_2_2.py +++ b/src/python_testing/TC_EWATERHTR_2_2.py @@ -23,7 +23,7 @@ # test-runner-run/run1/app: ${ALL_CLUSTERS_APP} # test-runner-run/run1/factoryreset: True # test-runner-run/run1/quiet: True -# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json --enable-key 000102030405060708090a0b0c0d0e0f --featureSet 0x00 +# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json --enable-key 000102030405060708090a0b0c0d0e0f # test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --hex-arg enableKey:000102030405060708090a0b0c0d0e0f --endpoint 1 --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto # === END CI TEST ARGUMENTS === @@ -191,8 +191,8 @@ async def test_TC_EWATERHTR_2_2(self): heaterTypes = await self.read_whm_attribute_expect_success(attribute="HeaterTypes") asserts.assert_greater(heaterTypes, 0, f"Unexpected HeaterTypes value - expected {heaterTypes} > 0") - asserts.assert_less_equal(heaterTypes, Clusters.WaterHeaterManagement.Bitmaps.WaterHeaterTypeBitmap.kOther, - f"Unexpected HeaterTypes value - expected {heaterTypes} <= WaterHeaterTypeBitmap.kOther") + asserts.assert_less_equal(heaterTypes, Clusters.WaterHeaterManagement.Bitmaps.WaterHeaterHeatSourceBitmap.kOther, + f"Unexpected HeaterTypes value - expected {heaterTypes} <= WaterHeaterHeatSourceBitmap.kOther") self.step("4") await self.send_test_event_trigger_manual_mode_test_event() diff --git a/src/python_testing/TC_EWATERHTR_2_3.py b/src/python_testing/TC_EWATERHTR_2_3.py index b3ed4bdc252cb4..41500bd3ea586e 100644 --- a/src/python_testing/TC_EWATERHTR_2_3.py +++ b/src/python_testing/TC_EWATERHTR_2_3.py @@ -22,7 +22,7 @@ # test-runner-run/run1/app: ${ALL_CLUSTERS_APP} # test-runner-run/run1/factoryreset: True # test-runner-run/run1/quiet: True -# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json --enable-key 000102030405060708090a0b0c0d0e0f --featureSet 0x03 +# test-runner-run/run1/app-args: --discriminator 1234 --KVS kvs1 --trace-to json:${TRACE_APP}.json --enable-key 000102030405060708090a0b0c0d0e0f # test-runner-run/run1/script-args: --storage-path admin_storage.json --commissioning-method on-network --discriminator 1234 --passcode 20202021 --hex-arg enableKey:000102030405060708090a0b0c0d0e0f --endpoint 1 --trace-to json:${TRACE_TEST_JSON}.json --trace-to perfetto:${TRACE_TEST_PERFETTO}.perfetto # === END CI TEST ARGUMENTS === diff --git a/src/python_testing/TC_SEAR_1_2.py b/src/python_testing/TC_SEAR_1_2.py index 4ebb3342ee9bfc..a938ae6c31259f 100644 --- a/src/python_testing/TC_SEAR_1_2.py +++ b/src/python_testing/TC_SEAR_1_2.py @@ -89,8 +89,8 @@ async def read_and_validate_supported_areas(self, step): if len(self.mapid_list) > 0: asserts.assert_is_not(a.mapID, NullValue, f"SupportedAreas entry with AreaID({a.areaID}) should not have null MapID") - asserts.assert_is(a.mapID in self.mapid_list, - f"SupportedAreas entry with AreaID({a.areaID}) has unknown MapID({a.mapID})") + asserts.assert_true(a.mapID in self.mapid_list, + f"SupportedAreas entry with AreaID({a.areaID}) has unknown MapID({a.mapID})") k = f"mapID:{a.mapID} areaDesc:{a.areaDesc}" asserts.assert_true(k not in areadesc_s, f"SupportedAreas must have unique MapID({a.mapID}) + AreaDesc({a.areaDesc}) values!") diff --git a/src/python_testing/TC_SEAR_1_3.py b/src/python_testing/TC_SEAR_1_3.py index 0acee1b34cdeb8..3e8d60fa6a24f8 100644 --- a/src/python_testing/TC_SEAR_1_3.py +++ b/src/python_testing/TC_SEAR_1_3.py @@ -53,6 +53,8 @@ async def read_supported_areas(self, step): endpoint=self.endpoint, attribute=Clusters.ServiceArea.Attributes.SupportedAreas) logging.info("SupportedAreas: %s" % (supported_areas)) + self.supported_areas = supported_areas + return [a.areaID for a in supported_areas] async def read_selected_areas(self, step): diff --git a/src/python_testing/TC_TSTAT_4_2.py b/src/python_testing/TC_TSTAT_4_2.py index 90c820ebe07002..2b3169af9ae3bc 100644 --- a/src/python_testing/TC_TSTAT_4_2.py +++ b/src/python_testing/TC_TSTAT_4_2.py @@ -30,6 +30,7 @@ import logging import chip.clusters as Clusters +from chip import ChipDeviceCtrl # Needed before chip.FabricAdmin from chip.clusters import Globals from chip.clusters.Types import NullValue from chip.interaction_model import InteractionModelError, Status @@ -58,53 +59,76 @@ class TC_TSTAT_4_2(MatterBaseTest): - async def write_presets(self, endpoint, presets) -> Status: - result = await self.default_controller.WriteAttribute(self.dut_node_id, [(endpoint, cluster.Attributes.Presets(presets))]) - return result[0].Status - - async def send_edit_atomic_request_begin_command(self, - endpoint: int = None, - expected_status: Status = Status.Success): + def check_atomic_response(self, response: object, expected_status: Status = Status.Success, + expected_overall_status: Status = Status.Success, + expected_preset_status: Status = Status.Success): + asserts.assert_equal(expected_status, Status.Success, "We expected we had a valid response") + asserts.assert_equal(response.statusCode, expected_overall_status, "Response should have the right overall status") + found_preset_status = False + for attrStatus in response.attributeStatus: + if attrStatus.attributeID == cluster.Attributes.Presets.attribute_id: + asserts.assert_equal(attrStatus.statusCode, expected_preset_status, + "Preset attribute should have the right status") + found_preset_status = True + asserts.assert_true(found_preset_status, "Preset attribute should have a status") + + async def write_presets(self, + endpoint, + presets, + dev_ctrl: ChipDeviceCtrl = None, + expected_status: Status = Status.Success) -> Status: + if dev_ctrl is None: + dev_ctrl = self.default_controller + result = await dev_ctrl.WriteAttribute(self.dut_node_id, [(endpoint, cluster.Attributes.Presets(presets))]) + status = result[0].Status + asserts.assert_equal(status, expected_status, f"Presets write returned {status.name}; expected {expected_status.name}") + return status + + async def send_atomic_request_begin_command(self, + dev_ctrl: ChipDeviceCtrl = None, + endpoint: int = None, + expected_status: Status = Status.Success, + expected_overall_status: Status = Status.Success, + expected_preset_status: Status = Status.Success): try: - await self.send_single_cmd(cmd=cluster.Commands.AtomicRequest(requestType=Globals.Enums.AtomicRequestTypeEnum.kBeginWrite, - attributeRequests=[ - cluster.Attributes.Presets.attribute_id], - timeout=1800), - endpoint=endpoint) - asserts.assert_equal(expected_status, Status.Success) + response = await self.send_single_cmd(cmd=cluster.Commands.AtomicRequest(requestType=Globals.Enums.AtomicRequestTypeEnum.kBeginWrite, + attributeRequests=[ + cluster.Attributes.Presets.attribute_id], + timeout=1800), + dev_ctrl=dev_ctrl, + endpoint=endpoint) + self.check_atomic_response(response, expected_status, expected_overall_status, expected_preset_status) except InteractionModelError as e: asserts.assert_equal(e.status, expected_status, "Unexpected error returned") - async def send_edit_atomic_request_commit_command(self, - endpoint: int = None, - expected_status: Status = Status.Success, - expected_overall_status: Status = Status.Success, - expected_preset_status: Status = Status.Success): + async def send_atomic_request_commit_command(self, + dev_ctrl: ChipDeviceCtrl = None, + endpoint: int = None, + expected_status: Status = Status.Success, + expected_overall_status: Status = Status.Success, + expected_preset_status: Status = Status.Success): try: response = await self.send_single_cmd(cmd=cluster.Commands.AtomicRequest(requestType=Globals.Enums.AtomicRequestTypeEnum.kCommitWrite, attributeRequests=[cluster.Attributes.Presets.attribute_id, cluster.Attributes.Schedules.attribute_id]), + dev_ctrl=dev_ctrl, endpoint=endpoint) - asserts.assert_equal(expected_status, Status.Success, "We expected we had a valid commit command") - asserts.assert_equal(response.statusCode, expected_overall_status, "Commit should have the right overall status") - found_preset_status = False - for attrStatus in response.attributeStatus: - if attrStatus.attributeID == cluster.Attributes.Presets.attribute_id: - asserts.assert_equal(attrStatus.statusCode, expected_preset_status, - "Preset attribute commit should have the right status") - found_preset_status = True - asserts.assert_true(found_preset_status, "Preset attribute commit should have a status") + self.check_atomic_response(response, expected_status, expected_overall_status, expected_preset_status) except InteractionModelError as e: asserts.assert_equal(e.status, expected_status, "Unexpected error returned") - async def send_edit_atomic_request_rollback_command(self, - endpoint: int = None, - expected_status: Status = Status.Success): + async def send_atomic_request_rollback_command(self, + dev_ctrl: ChipDeviceCtrl = None, + endpoint: int = None, + expected_status: Status = Status.Success, + expected_overall_status: Status = Status.Success, + expected_preset_status: Status = Status.Success): try: - await self.send_single_cmd(cmd=cluster.Commands.AtomicRequest(requestType=Globals.Enums.AtomicRequestTypeEnum.kRollbackWrite, - attributeRequests=[cluster.Attributes.Presets.attribute_id, cluster.Attributes.Schedules.attribute_id]), - endpoint=endpoint) - asserts.assert_equal(expected_status, Status.Success) + response = await self.send_single_cmd(cmd=cluster.Commands.AtomicRequest(requestType=Globals.Enums.AtomicRequestTypeEnum.kRollbackWrite, + attributeRequests=[cluster.Attributes.Presets.attribute_id, cluster.Attributes.Schedules.attribute_id]), + dev_ctrl=dev_ctrl, + endpoint=endpoint) + self.check_atomic_response(response, expected_status, expected_overall_status, expected_preset_status) except InteractionModelError as e: asserts.assert_equal(e.status, expected_status, "Unexpected error returned") @@ -134,39 +158,57 @@ def steps_TC_TSTAT_4_2(self) -> list[TestStep]: is_commissioning=True), TestStep("2", "TH writes to the Presets attribute without calling the AtomicRequest command", " Verify that the write request returns INVALID_IN_STATE error since the client didn't send a request to edit the presets by calling AtomicRequest command."), - TestStep("3", "TH writes to the Presets attribute after calling the AtomicRequest command but doesn't call CommitPresetsSchedulesRequest to commit", - "Verify that the Presets attribute was not updated since CommitPresetsSchedulesRequest command was not called."), - TestStep("4", "TH writes to the Presets attribute after calling the AtomicRequest command and calls CommitPresetsSchedulesRequest to commit", + TestStep("3", "TH writes to the Presets attribute after calling the AtomicRequest begin command but doesn't call AtomicRequest commit", + "Verify that the Presets attribute was not updated since AtomicRequest commit command was not called."), + TestStep("4", "TH writes to the Presets attribute after calling the AtomicRequest begin command and calls AtomicRequest commit", "Verify that the Presets attribute was updated with new presets."), TestStep("5", "TH writes to the Presets attribute with a built-in preset removed", - "Verify that the CommitPresetsSchedulesRequest returned UNSUPPORTED_ACCESS (0x7e)."), + "Verify that the AtomicRequest commit returned CONSTRAINT_ERROR (0x87)."), TestStep("6", "TH writes to the Presets attribute with a preset removed whose handle matches the value in the ActivePresetHandle attribute", - "Verify that the CommitPresetsSchedulesRequest returned INVALID_IN_STATE (0xcb)."), + "Verify that the AtomicRequest commit returned INVALID_IN_STATE (0xcb)."), TestStep("7", "TH writes to the Presets attribute with a built-in preset modified to be not built-in", - "Verify that the CommitPresetsSchedulesRequest returned UNSUPPORTED_ACCESS (0x7e)."), + "Verify that the AtomicRequest commit returned CONSTRAINT_ERROR (0x87)."), TestStep("8", "TH writes to the Presets attribute with a new preset having builtIn set to true", - "Verify that the CommitPresetsSchedulesRequest returned CONSTRAINT_ERROR (0x87)."), + "Verify that the AtomicRequest commit returned CONSTRAINT_ERROR (0x87)."), TestStep("9", "TH writes to the Presets attribute with a new preset having a preset handle that doesn't exist in the Presets attribute", - "Verify that the CommitPresetsSchedulesRequest returned NOT_FOUND (0x8b)."), + "Verify that the AtomicRequest commit returned NOT_FOUND (0x8b)."), TestStep("10", "TH writes to the Presets attribute with duplicate presets", - "Verify that the CommitPresetsSchedulesRequest returned CONSTRAINT_ERROR (0x87)."), + "Verify that the AtomicRequest commit returned CONSTRAINT_ERROR (0x87)."), TestStep("11", "TH writes to the Presets attribute with a non built-in preset modified to be built-in", - "Verify that the CommitPresetsSchedulesRequest returned UNSUPPORTED_ACCESS (0x7e)."), + "Verify that the AtomicRequest commit returned CONSTRAINT_ERROR (0x87)."), TestStep("12", "TH writes to the Presets attribute with a preset that doesn't support names in the PresetTypeFeatures bitmap but has a name", - "Verify that the CommitPresetsSchedulesRequest returned CONSTRAINT_ERROR (0x87)."), - TestStep("13", "TH writes to the Presets attribute but calls the CancelPresetsSchedulesEditRequest command to cancel the edit request", - "Verify that the edit request was cancelled"), + "Verify that the AtomicRequest commit returned CONSTRAINT_ERROR (0x87)."), + TestStep("13", "TH writes to the Presets attribute but calls the AtomicRequest rollback command to cancel the edit request", + "Verify that the edit request was rolled back"), + TestStep("14", "TH starts an atomic write, and TH2 attempts to open an atomic write before TH is complete", + "Verify that the atomic request is rejected"), + TestStep("15", "TH starts an atomic write, and TH2 attempts to write to presets", + "Verify that the write request is rejected"), + TestStep("16", "TH starts an atomic write, and before it's complete, TH2 removes TH's fabric; TH2 then opens an atomic write", + "Verify that the atomic request is successful"), ] return steps - @async_test_body + @ async_test_body async def test_TC_TSTAT_4_2(self): endpoint = self.user_params.get("endpoint", 1) self.step("1") # Commission DUT - already done + logger.info("Commissioning under second controller") + params = await self.default_controller.OpenCommissioningWindow( + nodeid=self.dut_node_id, timeout=600, iteration=10000, discriminator=1234, option=1) + + secondary_authority = self.certificate_authority_manager.NewCertificateAuthority() + secondary_fabric_admin = secondary_authority.NewFabricAdmin(vendorId=0xFFF1, fabricId=2) + secondary_controller = secondary_fabric_admin.NewController(nodeId=112233) + + await secondary_controller.CommissionOnNetwork( + nodeId=self.dut_node_id, setupPinCode=params.setupPinCode, + filterType=ChipDeviceCtrl.DiscoveryFilterType.LONG_DISCRIMINATOR, filter=1234) + self.step("2") if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050")): presets = await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=cluster.Attributes.Presets) @@ -174,13 +216,12 @@ async def test_TC_TSTAT_4_2(self): asserts.assert_equal(presets, initial_presets, "Presets do not match initial value") # Write to the presets attribute without calling AtomicRequest command - status = await self.write_presets(endpoint=endpoint, presets=new_presets) - status_ok = (status == Status.InvalidInState) - asserts.assert_true(status_ok, "Presets write did not return InvalidInState as expected") + await self.write_presets(endpoint=endpoint, presets=new_presets, expected_status=Status.InvalidInState) self.step("3") - if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): - await self.send_edit_atomic_request_begin_command() + + if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.CFE.Rsp")): + await self.send_atomic_request_begin_command() # Write to the presets attribute after calling AtomicRequest command status = await self.write_presets(endpoint=endpoint, presets=new_presets) @@ -192,7 +233,7 @@ async def test_TC_TSTAT_4_2(self): logger.info(f"Rx'd Presets: {presets}") asserts.assert_equal(presets, new_presets_with_handle, "Presets were updated, as expected") - await self.send_edit_atomic_request_rollback_command() + await self.send_atomic_request_rollback_command() # Read the presets attribute and verify it has been properly rolled back presets = await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=cluster.Attributes.Presets) @@ -202,15 +243,13 @@ async def test_TC_TSTAT_4_2(self): if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): # Send the AtomicRequest begin command - await self.send_edit_atomic_request_begin_command() + await self.send_atomic_request_begin_command() # Write to the presets attribute after calling AtomicRequest command - status = await self.write_presets(endpoint=endpoint, presets=new_presets) - status_ok = (status == Status.Success) - asserts.assert_true(status_ok, "Presets write did not return Success as expected") + await self.write_presets(endpoint=endpoint, presets=new_presets) # Send the AtomicRequest commit command - await self.send_edit_atomic_request_commit_command() + await self.send_atomic_request_commit_command() # Read the presets attribute and verify it was updated since AtomicRequest commit was called after writing presets presets = await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=cluster.Attributes.Presets) @@ -221,17 +260,15 @@ async def test_TC_TSTAT_4_2(self): if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): # Send the AtomicRequest begin command - await self.send_edit_atomic_request_begin_command() + await self.send_atomic_request_begin_command() # Write to the presets attribute after removing a built in preset from the list. Remove the first entry. test_presets = new_presets_with_handle.copy() test_presets.pop(0) - status = await self.write_presets(endpoint=endpoint, presets=test_presets) - status_ok = (status == Status.Success) - asserts.assert_true(status_ok, "Presets write did not return Success as expected") + await self.write_presets(endpoint=endpoint, presets=test_presets) # Send the AtomicRequest commit command and expect ConstraintError for presets. - await self.send_edit_atomic_request_commit_command(expected_overall_status=Status.Failure, expected_preset_status=Status.ConstraintError) + await self.send_atomic_request_commit_command(expected_overall_status=Status.Failure, expected_preset_status=Status.ConstraintError) self.step("6") if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.C06.Rsp") and self.check_pics("TSTAT.S.Cfe.Rsp")): @@ -245,144 +282,164 @@ async def test_TC_TSTAT_4_2(self): asserts.assert_equal(activePresetHandle, b'\x03', "Active preset handle was not updated as expected") # Send the AtomicRequest begin command - await self.send_edit_atomic_request_begin_command() + await self.send_atomic_request_begin_command() # Write to the presets attribute after removing the preset that was set as the active preset handle. Remove the last entry with preset handle (b'\x03') test_presets = new_presets_with_handle.copy() del test_presets[-1] - status = await self.write_presets(endpoint=endpoint, presets=test_presets) - status_ok = (status == Status.Success) - asserts.assert_true(status_ok, "Presets write did not return Success as expected") + await self.write_presets(endpoint=endpoint, presets=test_presets) # Send the AtomicRequest commit command and expect InvalidInState for presets. - await self.send_edit_atomic_request_commit_command(expected_overall_status=Status.Failure, expected_preset_status=Status.InvalidInState) + await self.send_atomic_request_commit_command(expected_overall_status=Status.Failure, expected_preset_status=Status.InvalidInState) self.step("7") if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): # Send the AtomicRequest begin command - await self.send_edit_atomic_request_begin_command() + await self.send_atomic_request_begin_command() # Write to the presets attribute after setting the builtIn flag to False for preset with handle (b'\x01') test_presets = copy.deepcopy(new_presets_with_handle) test_presets[0].builtIn = False - status = await self.write_presets(endpoint=endpoint, presets=test_presets) - asserts.assert_equal(status, Status.ConstraintError, - "Presets write should return ConstraintError, because BuiltIn values do not match") + await self.write_presets(endpoint=endpoint, presets=test_presets, expected_status=Status.ConstraintError) # Clear state for next test. - await self.send_edit_atomic_request_rollback_command() + await self.send_atomic_request_rollback_command() self.step("8") if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): # Send the AtomicRequest begin command - await self.send_edit_atomic_request_begin_command() + await self.send_atomic_request_begin_command() # Write to the presets attribute after adding a preset with builtIn set to True test_presets = copy.deepcopy(new_presets_with_handle) test_presets.append(cluster.Structs.PresetStruct(presetHandle=NullValue, presetScenario=cluster.Enums.PresetScenarioEnum.kWake, name="Wake", coolingSetpoint=2800, heatingSetpoint=1800, builtIn=True)) - status = await self.write_presets(endpoint=endpoint, presets=test_presets) - status_ok = (status == Status.Success) - asserts.assert_equal(status, Status.ConstraintError, - "Presets write should return ConstraintError, since we are trying to add a new built-in preset") + status = await self.write_presets(endpoint=endpoint, presets=test_presets, expected_status=Status.ConstraintError) # Clear state for next test. - await self.send_edit_atomic_request_rollback_command() + await self.send_atomic_request_rollback_command() self.step("9") if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): # Send the AtomicRequest begin command - await self.send_edit_atomic_request_begin_command() + await self.send_atomic_request_begin_command() # Write to the presets attribute after adding a preset with a preset handle that doesn't exist in Presets attribute test_presets = copy.deepcopy(new_presets_with_handle) test_presets.append(cluster.Structs.PresetStruct(presetHandle=b'\x08', presetScenario=cluster.Enums.PresetScenarioEnum.kWake, name="Wake", coolingSetpoint=2800, heatingSetpoint=1800, builtIn=True)) - status = await self.write_presets(endpoint=endpoint, presets=test_presets) - asserts.assert_equal(status, Status.NotFound, - "Presets write should return NotFound, since we are trying to modify non-existent preset") + status = await self.write_presets(endpoint=endpoint, presets=test_presets, expected_status=Status.NotFound) # Clear state for next test. - await self.send_edit_atomic_request_rollback_command() + await self.send_atomic_request_rollback_command() self.step("10") if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): # Send the AtomicRequest begin command - await self.send_edit_atomic_request_begin_command() + await self.send_atomic_request_begin_command() # Write to the presets attribute after adding a duplicate preset with handle (b'\x03') test_presets = copy.deepcopy(new_presets_with_handle) test_presets.append(cluster.Structs.PresetStruct( presetHandle=b'\x03', presetScenario=cluster.Enums.PresetScenarioEnum.kSleep, name="Sleep", coolingSetpoint=2700, heatingSetpoint=1900, builtIn=False)) - status = await self.write_presets(endpoint=endpoint, presets=test_presets) - asserts.assert_equal(status, Status.ConstraintError, - "Presets write should return ConstraintError, since we have duplicated presets") + await self.write_presets(endpoint=endpoint, presets=test_presets, expected_status=Status.ConstraintError) # Clear state for next test. - await self.send_edit_atomic_request_rollback_command() + await self.send_atomic_request_rollback_command() self.step("11") if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): # Send the AtomicRequest begin command - await self.send_edit_atomic_request_begin_command() + await self.send_atomic_request_begin_command() # Write to the presets attribute after setting the builtIn flag to True for preset with handle (b'\x03') test_presets = copy.deepcopy(new_presets_with_handle) test_presets[2].builtIn = True - status = await self.write_presets(endpoint=endpoint, presets=test_presets) - asserts.assert_equal(status, Status.ConstraintError, - "Presets write should return ConstraintError, since we are trying to change whether a preset is BuiltIn") + await self.write_presets(endpoint=endpoint, presets=test_presets, expected_status=Status.ConstraintError) # Clear state for next test. - await self.send_edit_atomic_request_rollback_command() + await self.send_atomic_request_rollback_command() self.step("12") if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): # Send the AtomicRequest begin command - await self.send_edit_atomic_request_begin_command() + await self.send_atomic_request_begin_command() # Write to the presets attribute after setting a name for preset with handle (b'\x01') that doesn't support names test_presets = copy.deepcopy(new_presets_with_handle) test_presets[0].name = "Occupied" - status = await self.write_presets(endpoint=endpoint, presets=test_presets) - asserts.assert_equal(status, Status.ConstraintError, - "Presets write should return ConstraintError, since we are trying to set a name for a preset that does not support that") + await self.write_presets(endpoint=endpoint, presets=test_presets, expected_status=Status.ConstraintError) # Clear state for next test. - await self.send_edit_atomic_request_rollback_command() + await self.send_atomic_request_rollback_command() self.step("13") if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.Cfe.Rsp")): # Send the AtomicRequest begin command - await self.send_edit_atomic_request_begin_command() + await self.send_atomic_request_begin_command() # Write to the presets attribute with a new valid preset added test_presets = copy.deepcopy(new_presets_with_handle) test_presets.append(cluster.Structs.PresetStruct(presetHandle=NullValue, presetScenario=cluster.Enums.PresetScenarioEnum.kWake, name="Wake", coolingSetpoint=2800, heatingSetpoint=1800, builtIn=False)) - status = await self.write_presets(endpoint=endpoint, presets=test_presets) - status_ok = (status == Status.Success) - asserts.assert_equal(status, Status.Success, "Presets write did not return Success as expected") + await self.write_presets(endpoint=endpoint, presets=test_presets) # Roll back - await self.send_edit_atomic_request_rollback_command() + await self.send_atomic_request_rollback_command() # Send the AtomicRequest commit command and expect InvalidInState as the previous edit request was cancelled - await self.send_edit_atomic_request_commit_command(expected_status=Status.InvalidInState) + await self.send_atomic_request_commit_command(expected_status=Status.InvalidInState) + + self.step("14") + if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.CFE.Rsp")): + + # Send the AtomicRequest begin command + await self.send_atomic_request_begin_command() + + # Send the AtomicRequest begin command from separate controller, which should receive busy + status = await self.send_atomic_request_begin_command(dev_ctrl=secondary_controller, expected_overall_status=Status.Failure, expected_preset_status=Status.Busy) + + # Roll back + await self.send_atomic_request_rollback_command() + + self.step("15") + if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.CFE.Rsp")): + # Send the AtomicRequest begin command from the secondary controller + await self.send_atomic_request_begin_command() + + await self.write_presets(endpoint=endpoint, presets=test_presets, dev_ctrl=secondary_controller, expected_status=Status.Busy) + + # Roll back + await self.send_atomic_request_rollback_command() + + self.step("16") + if self.pics_guard(self.check_pics("TSTAT.S.F08") and self.check_pics("TSTAT.S.A0050") and self.check_pics("TSTAT.S.CFE.Rsp")): + + # Send the AtomicRequest begin command from the secondary controller + await self.send_atomic_request_begin_command(dev_ctrl=secondary_controller) + + # Primary controller removes the second fabric + await self.send_single_cmd(Clusters.OperationalCredentials.Commands.RemoveFabric(fabricIndex=2), endpoint=0) + + # Send the AtomicRequest begin command from primary controller, which should succeed, as the secondary controller's atomic write state has been cleared + status = await self.send_atomic_request_begin_command() + + # Roll back + await self.send_atomic_request_rollback_command() # TODO: Add tests for the total number of Presets exceeds the NumberOfPresets supported. Also Add tests for adding presets with preset scenario not present in PresetTypes. diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h b/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h index a6121b3681348e..571439625e121a 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-enums.h @@ -2631,18 +2631,8 @@ enum class Feature : uint32_t kTankPercent = 0x2, }; -// Bitmap for WaterHeaterDemandBitmap -enum class WaterHeaterDemandBitmap : uint8_t -{ - kImmersionElement1 = 0x1, - kImmersionElement2 = 0x2, - kHeatPump = 0x4, - kBoiler = 0x8, - kOther = 0x10, -}; - -// Bitmap for WaterHeaterTypeBitmap -enum class WaterHeaterTypeBitmap : uint8_t +// Bitmap for WaterHeaterHeatSourceBitmap +enum class WaterHeaterHeatSourceBitmap : uint8_t { kImmersionElement1 = 0x1, kImmersionElement2 = 0x2, diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp index 90eddd9b2f1f0d..20fdd126514076 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp @@ -15168,9 +15168,9 @@ CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) } // namespace ElectricalEnergyMeasurement namespace WaterHeaterManagement { +namespace Structs { -namespace Commands { -namespace Boost { +namespace WaterHeaterBoostInfoStruct { CHIP_ERROR Type::Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const { DataModel::WrappedStructEncoder encoder{ aWriter, aTag }; @@ -15228,6 +15228,44 @@ CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) ReturnErrorOnFailure(err); } } + +} // namespace WaterHeaterBoostInfoStruct +} // namespace Structs + +namespace Commands { +namespace Boost { +CHIP_ERROR Type::Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const +{ + DataModel::WrappedStructEncoder encoder{ aWriter, aTag }; + encoder.Encode(to_underlying(Fields::kBoostInfo), boostInfo); + return encoder.Finalize(); +} + +CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) +{ + detail::StructDecodeIterator __iterator(reader); + while (true) + { + auto __element = __iterator.Next(); + if (std::holds_alternative(__element)) + { + return std::get(__element); + } + + CHIP_ERROR err = CHIP_NO_ERROR; + const uint8_t __context_tag = std::get(__element); + + if (__context_tag == to_underlying(Fields::kBoostInfo)) + { + err = DataModel::Decode(reader, boostInfo); + } + else + { + } + + ReturnErrorOnFailure(err); + } +} } // namespace Boost. namespace CancelBoost { CHIP_ERROR Type::Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const @@ -15286,7 +15324,64 @@ CHIP_ERROR TypeInfo::DecodableType::Decode(TLV::TLVReader & reader, const Concre } } // namespace Attributes -namespace Events {} // namespace Events +namespace Events { +namespace BoostStarted { +CHIP_ERROR Type::Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const +{ + TLV::TLVType outer; + ReturnErrorOnFailure(aWriter.StartContainer(aTag, TLV::kTLVType_Structure, outer)); + ReturnErrorOnFailure(DataModel::Encode(aWriter, TLV::ContextTag(Fields::kBoostInfo), boostInfo)); + return aWriter.EndContainer(outer); +} + +CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) +{ + detail::StructDecodeIterator __iterator(reader); + while (true) + { + auto __element = __iterator.Next(); + if (std::holds_alternative(__element)) + { + return std::get(__element); + } + + CHIP_ERROR err = CHIP_NO_ERROR; + const uint8_t __context_tag = std::get(__element); + + if (__context_tag == to_underlying(Fields::kBoostInfo)) + { + err = DataModel::Decode(reader, boostInfo); + } + else + { + } + + ReturnErrorOnFailure(err); + } +} +} // namespace BoostStarted. +namespace BoostEnded { +CHIP_ERROR Type::Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const +{ + TLV::TLVType outer; + ReturnErrorOnFailure(aWriter.StartContainer(aTag, TLV::kTLVType_Structure, outer)); + return aWriter.EndContainer(outer); +} + +CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) +{ + detail::StructDecodeIterator __iterator(reader); + while (true) + { + auto __element = __iterator.Next(); + if (std::holds_alternative(__element)) + { + return std::get(__element); + } + } +} +} // namespace BoostEnded. +} // namespace Events } // namespace WaterHeaterManagement namespace DemandResponseLoadControl { diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h index 27ee806af6e9a9..f0f1dea1de536d 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h @@ -21449,6 +21449,39 @@ struct DecodableType } // namespace Events } // namespace ElectricalEnergyMeasurement namespace WaterHeaterManagement { +namespace Structs { +namespace WaterHeaterBoostInfoStruct { +enum class Fields : uint8_t +{ + kDuration = 0, + kOneShot = 1, + kEmergencyBoost = 2, + kTemporarySetpoint = 3, + kTargetPercentage = 4, + kTargetReheat = 5, +}; + +struct Type +{ +public: + uint32_t duration = static_cast(0); + Optional oneShot; + Optional emergencyBoost; + Optional temporarySetpoint; + Optional targetPercentage; + Optional targetReheat; + + CHIP_ERROR Decode(TLV::TLVReader & reader); + + static constexpr bool kIsFabricScoped = false; + + CHIP_ERROR Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const; +}; + +using DecodableType = Type; + +} // namespace WaterHeaterBoostInfoStruct +} // namespace Structs namespace Commands { // Forward-declarations so we can reference these later. @@ -21469,12 +21502,7 @@ namespace Commands { namespace Boost { enum class Fields : uint8_t { - kDuration = 0, - kOneShot = 1, - kEmergencyBoost = 2, - kTemporarySetpoint = 3, - kTargetPercentage = 4, - kTargetReheat = 5, + kBoostInfo = 0, }; struct Type @@ -21484,12 +21512,7 @@ struct Type static constexpr CommandId GetCommandId() { return Commands::Boost::Id; } static constexpr ClusterId GetClusterId() { return Clusters::WaterHeaterManagement::Id; } - uint32_t duration = static_cast(0); - Optional oneShot; - Optional emergencyBoost; - Optional temporarySetpoint; - Optional targetPercentage; - Optional targetReheat; + Structs::WaterHeaterBoostInfoStruct::Type boostInfo; CHIP_ERROR Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const; @@ -21504,12 +21527,7 @@ struct DecodableType static constexpr CommandId GetCommandId() { return Commands::Boost::Id; } static constexpr ClusterId GetClusterId() { return Clusters::WaterHeaterManagement::Id; } - uint32_t duration = static_cast(0); - Optional oneShot; - Optional emergencyBoost; - Optional temporarySetpoint; - Optional targetPercentage; - Optional targetReheat; + Structs::WaterHeaterBoostInfoStruct::DecodableType boostInfo; CHIP_ERROR Decode(TLV::TLVReader & reader); }; }; // namespace Boost @@ -21548,9 +21566,9 @@ namespace Attributes { namespace HeaterTypes { struct TypeInfo { - using Type = chip::BitMask; - using DecodableType = chip::BitMask; - using DecodableArgType = chip::BitMask; + using Type = chip::BitMask; + using DecodableType = chip::BitMask; + using DecodableArgType = chip::BitMask; static constexpr ClusterId GetClusterId() { return Clusters::WaterHeaterManagement::Id; } static constexpr AttributeId GetAttributeId() { return Attributes::HeaterTypes::Id; } @@ -21560,9 +21578,9 @@ struct TypeInfo namespace HeatDemand { struct TypeInfo { - using Type = chip::BitMask; - using DecodableType = chip::BitMask; - using DecodableArgType = chip::BitMask; + using Type = chip::BitMask; + using DecodableType = chip::BitMask; + using DecodableArgType = chip::BitMask; static constexpr ClusterId GetClusterId() { return Clusters::WaterHeaterManagement::Id; } static constexpr AttributeId GetAttributeId() { return Attributes::HeatDemand::Id; } @@ -21663,9 +21681,9 @@ struct TypeInfo CHIP_ERROR Decode(TLV::TLVReader & reader, const ConcreteAttributePath & path); Attributes::HeaterTypes::TypeInfo::DecodableType heaterTypes = - static_cast>(0); + static_cast>(0); Attributes::HeatDemand::TypeInfo::DecodableType heatDemand = - static_cast>(0); + static_cast>(0); Attributes::TankVolume::TypeInfo::DecodableType tankVolume = static_cast(0); Attributes::EstimatedHeatRequired::TypeInfo::DecodableType estimatedHeatRequired = static_cast(0); Attributes::TankPercentage::TypeInfo::DecodableType tankPercentage = static_cast(0); @@ -21680,6 +21698,69 @@ struct TypeInfo }; }; } // namespace Attributes +namespace Events { +namespace BoostStarted { +static constexpr PriorityLevel kPriorityLevel = PriorityLevel::Info; + +enum class Fields : uint8_t +{ + kBoostInfo = 0, +}; + +struct Type +{ +public: + static constexpr PriorityLevel GetPriorityLevel() { return kPriorityLevel; } + static constexpr EventId GetEventId() { return Events::BoostStarted::Id; } + static constexpr ClusterId GetClusterId() { return Clusters::WaterHeaterManagement::Id; } + static constexpr bool kIsFabricScoped = false; + + Structs::WaterHeaterBoostInfoStruct::Type boostInfo; + + CHIP_ERROR Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const; +}; + +struct DecodableType +{ +public: + static constexpr PriorityLevel GetPriorityLevel() { return kPriorityLevel; } + static constexpr EventId GetEventId() { return Events::BoostStarted::Id; } + static constexpr ClusterId GetClusterId() { return Clusters::WaterHeaterManagement::Id; } + + Structs::WaterHeaterBoostInfoStruct::DecodableType boostInfo; + + CHIP_ERROR Decode(TLV::TLVReader & reader); +}; +} // namespace BoostStarted +namespace BoostEnded { +static constexpr PriorityLevel kPriorityLevel = PriorityLevel::Info; + +enum class Fields : uint8_t +{ +}; + +struct Type +{ +public: + static constexpr PriorityLevel GetPriorityLevel() { return kPriorityLevel; } + static constexpr EventId GetEventId() { return Events::BoostEnded::Id; } + static constexpr ClusterId GetClusterId() { return Clusters::WaterHeaterManagement::Id; } + static constexpr bool kIsFabricScoped = false; + + CHIP_ERROR Encode(TLV::TLVWriter & aWriter, TLV::Tag aTag) const; +}; + +struct DecodableType +{ +public: + static constexpr PriorityLevel GetPriorityLevel() { return kPriorityLevel; } + static constexpr EventId GetEventId() { return Events::BoostEnded::Id; } + static constexpr ClusterId GetClusterId() { return Clusters::WaterHeaterManagement::Id; } + + CHIP_ERROR Decode(TLV::TLVReader & reader); +}; +} // namespace BoostEnded +} // namespace Events } // namespace WaterHeaterManagement namespace DemandResponseLoadControl { namespace Structs { diff --git a/zzz_generated/app-common/app-common/zap-generated/ids/Events.h b/zzz_generated/app-common/app-common/zap-generated/ids/Events.h index f8243c506f906d..4227a6ab527ba2 100644 --- a/zzz_generated/app-common/app-common/zap-generated/ids/Events.h +++ b/zzz_generated/app-common/app-common/zap-generated/ids/Events.h @@ -443,6 +443,20 @@ static constexpr EventId Id = 0x00000001; } // namespace Events } // namespace ElectricalEnergyMeasurement +namespace WaterHeaterManagement { +namespace Events { + +namespace BoostStarted { +static constexpr EventId Id = 0x00000000; +} // namespace BoostStarted + +namespace BoostEnded { +static constexpr EventId Id = 0x00000001; +} // namespace BoostEnded + +} // namespace Events +} // namespace WaterHeaterManagement + namespace DemandResponseLoadControl { namespace Events { diff --git a/zzz_generated/chip-tool/zap-generated/cluster/Commands.h b/zzz_generated/chip-tool/zap-generated/cluster/Commands.h index 85a45a057b7eab..57259e33d69d44 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/Commands.h +++ b/zzz_generated/chip-tool/zap-generated/cluster/Commands.h @@ -6731,6 +6731,8 @@ class ValveConfigurationAndControlClose : public ClusterCommand | * ClusterRevision | 0xFFFD | |------------------------------------------------------------------------------| | Events: | | +| * BoostStarted | 0x0000 | +| * BoostEnded | 0x0001 | \*----------------------------------------------------------------------------*/ /* @@ -6739,14 +6741,10 @@ class ValveConfigurationAndControlClose : public ClusterCommand class WaterHeaterManagementBoost : public ClusterCommand { public: - WaterHeaterManagementBoost(CredentialIssuerCommands * credsIssuerConfig) : ClusterCommand("boost", credsIssuerConfig) + WaterHeaterManagementBoost(CredentialIssuerCommands * credsIssuerConfig) : + ClusterCommand("boost", credsIssuerConfig), mComplex_BoostInfo(&mRequest.boostInfo) { - AddArgument("Duration", 0, UINT32_MAX, &mRequest.duration); - AddArgument("OneShot", 0, 1, &mRequest.oneShot); - AddArgument("EmergencyBoost", 0, 1, &mRequest.emergencyBoost); - AddArgument("TemporarySetpoint", INT16_MIN, INT16_MAX, &mRequest.temporarySetpoint); - AddArgument("TargetPercentage", 0, UINT8_MAX, &mRequest.targetPercentage); - AddArgument("TargetReheat", 0, UINT8_MAX, &mRequest.targetReheat); + AddArgument("BoostInfo", &mComplex_BoostInfo); ClusterCommand::AddArguments(); } @@ -6773,6 +6771,7 @@ class WaterHeaterManagementBoost : public ClusterCommand private: chip::app::Clusters::WaterHeaterManagement::Commands::Boost::Type mRequest; + TypedComplexArgument mComplex_BoostInfo; }; /* @@ -21293,9 +21292,9 @@ void registerClusterWaterHeaterManagement(Commands & commands, CredentialIssuerC make_unique(Id, "feature-map", Attributes::FeatureMap::Id, credsIssuerConfig), // make_unique(Id, "cluster-revision", Attributes::ClusterRevision::Id, credsIssuerConfig), // make_unique>(Id, credsIssuerConfig), // - make_unique>>( + make_unique>>( Id, "heater-types", 0, UINT8_MAX, Attributes::HeaterTypes::Id, WriteCommandType::kForceWrite, credsIssuerConfig), // - make_unique>>( + make_unique>>( Id, "heat-demand", 0, UINT8_MAX, Attributes::HeatDemand::Id, WriteCommandType::kForceWrite, credsIssuerConfig), // make_unique>(Id, "tank-volume", 0, UINT16_MAX, Attributes::TankVolume::Id, WriteCommandType::kForceWrite, credsIssuerConfig), // @@ -21335,8 +21334,12 @@ void registerClusterWaterHeaterManagement(Commands & commands, CredentialIssuerC // // Events // - make_unique(Id, credsIssuerConfig), // - make_unique(Id, credsIssuerConfig), // + make_unique(Id, credsIssuerConfig), // + make_unique(Id, "boost-started", Events::BoostStarted::Id, credsIssuerConfig), // + make_unique(Id, "boost-ended", Events::BoostEnded::Id, credsIssuerConfig), // + make_unique(Id, credsIssuerConfig), // + make_unique(Id, "boost-started", Events::BoostStarted::Id, credsIssuerConfig), // + make_unique(Id, "boost-ended", Events::BoostEnded::Id, credsIssuerConfig), // }; commands.RegisterCluster(clusterName, clusterCommands); diff --git a/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.cpp b/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.cpp index d4f67568a7f746..202a6efcb06a01 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.cpp +++ b/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.cpp @@ -2897,6 +2897,73 @@ void ComplexArgumentParser::Finalize( ComplexArgumentParser::Finalize(request.endSystime); } +CHIP_ERROR +ComplexArgumentParser::Setup(const char * label, + chip::app::Clusters::WaterHeaterManagement::Structs::WaterHeaterBoostInfoStruct::Type & request, + Json::Value & value) +{ + VerifyOrReturnError(value.isObject(), CHIP_ERROR_INVALID_ARGUMENT); + + // Copy to track which members we already processed. + Json::Value valueCopy(value); + + ReturnErrorOnFailure( + ComplexArgumentParser::EnsureMemberExist("WaterHeaterBoostInfoStruct.duration", "duration", value.isMember("duration"))); + + char labelWithMember[kMaxLabelLength]; + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "duration"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.duration, value["duration"])); + valueCopy.removeMember("duration"); + + if (value.isMember("oneShot")) + { + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "oneShot"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.oneShot, value["oneShot"])); + } + valueCopy.removeMember("oneShot"); + + if (value.isMember("emergencyBoost")) + { + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "emergencyBoost"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.emergencyBoost, value["emergencyBoost"])); + } + valueCopy.removeMember("emergencyBoost"); + + if (value.isMember("temporarySetpoint")) + { + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "temporarySetpoint"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.temporarySetpoint, value["temporarySetpoint"])); + } + valueCopy.removeMember("temporarySetpoint"); + + if (value.isMember("targetPercentage")) + { + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "targetPercentage"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.targetPercentage, value["targetPercentage"])); + } + valueCopy.removeMember("targetPercentage"); + + if (value.isMember("targetReheat")) + { + snprintf(labelWithMember, sizeof(labelWithMember), "%s.%s", label, "targetReheat"); + ReturnErrorOnFailure(ComplexArgumentParser::Setup(labelWithMember, request.targetReheat, value["targetReheat"])); + } + valueCopy.removeMember("targetReheat"); + + return ComplexArgumentParser::EnsureNoMembersRemaining(label, valueCopy); +} + +void ComplexArgumentParser::Finalize( + chip::app::Clusters::WaterHeaterManagement::Structs::WaterHeaterBoostInfoStruct::Type & request) +{ + ComplexArgumentParser::Finalize(request.duration); + ComplexArgumentParser::Finalize(request.oneShot); + ComplexArgumentParser::Finalize(request.emergencyBoost); + ComplexArgumentParser::Finalize(request.temporarySetpoint); + ComplexArgumentParser::Finalize(request.targetPercentage); + ComplexArgumentParser::Finalize(request.targetReheat); +} + CHIP_ERROR ComplexArgumentParser::Setup(const char * label, chip::app::Clusters::DemandResponseLoadControl::Structs::HeatingSourceControlStruct::Type & request, diff --git a/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.h b/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.h index dfca56f4c3afba..ccce33ece63cd8 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.h +++ b/zzz_generated/chip-tool/zap-generated/cluster/ComplexArgumentParser.h @@ -343,6 +343,12 @@ static CHIP_ERROR Setup(const char * label, static void Finalize(chip::app::Clusters::ElectricalEnergyMeasurement::Structs::EnergyMeasurementStruct::Type & request); +static CHIP_ERROR Setup(const char * label, + chip::app::Clusters::WaterHeaterManagement::Structs::WaterHeaterBoostInfoStruct::Type & request, + Json::Value & value); + +static void Finalize(chip::app::Clusters::WaterHeaterManagement::Structs::WaterHeaterBoostInfoStruct::Type & request); + static CHIP_ERROR Setup(const char * label, chip::app::Clusters::DemandResponseLoadControl::Structs::HeatingSourceControlStruct::Type & request, Json::Value & value); diff --git a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp index 2eddf2f356bd07..aa122cb62da046 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp +++ b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.cpp @@ -2583,6 +2583,64 @@ CHIP_ERROR DataModelLogger::LogValue( return CHIP_NO_ERROR; } +CHIP_ERROR DataModelLogger::LogValue( + const char * label, size_t indent, + const chip::app::Clusters::WaterHeaterManagement::Structs::WaterHeaterBoostInfoStruct::DecodableType & value) +{ + DataModelLogger::LogString(label, indent, "{"); + { + CHIP_ERROR err = LogValue("Duration", indent + 1, value.duration); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'Duration'"); + return err; + } + } + { + CHIP_ERROR err = LogValue("OneShot", indent + 1, value.oneShot); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'OneShot'"); + return err; + } + } + { + CHIP_ERROR err = LogValue("EmergencyBoost", indent + 1, value.emergencyBoost); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'EmergencyBoost'"); + return err; + } + } + { + CHIP_ERROR err = LogValue("TemporarySetpoint", indent + 1, value.temporarySetpoint); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'TemporarySetpoint'"); + return err; + } + } + { + CHIP_ERROR err = LogValue("TargetPercentage", indent + 1, value.targetPercentage); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'TargetPercentage'"); + return err; + } + } + { + CHIP_ERROR err = LogValue("TargetReheat", indent + 1, value.targetReheat); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Struct truncated due to invalid value for 'TargetReheat'"); + return err; + } + } + DataModelLogger::LogString(indent, "}"); + + return CHIP_NO_ERROR; +} + CHIP_ERROR DataModelLogger::LogValue( const char * label, size_t indent, const chip::app::Clusters::DemandResponseLoadControl::Structs::HeatingSourceControlStruct::DecodableType & value) @@ -6891,6 +6949,30 @@ CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, return CHIP_NO_ERROR; } +CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, + const WaterHeaterManagement::Events::BoostStarted::DecodableType & value) +{ + DataModelLogger::LogString(label, indent, "{"); + { + CHIP_ERROR err = DataModelLogger::LogValue("BoostInfo", indent + 1, value.boostInfo); + if (err != CHIP_NO_ERROR) + { + DataModelLogger::LogString(indent + 1, "Event truncated due to invalid value for 'BoostInfo'"); + return err; + } + } + DataModelLogger::LogString(indent, "}"); + + return CHIP_NO_ERROR; +} +CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, + const WaterHeaterManagement::Events::BoostEnded::DecodableType & value) +{ + DataModelLogger::LogString(label, indent, "{"); + DataModelLogger::LogString(indent, "}"); + + return CHIP_NO_ERROR; +} CHIP_ERROR DataModelLogger::LogValue(const char * label, size_t indent, const DemandResponseLoadControl::Events::LoadControlEventStatusChange::DecodableType & value) { @@ -13547,12 +13629,12 @@ CHIP_ERROR DataModelLogger::LogAttribute(const chip::app::ConcreteDataAttributeP switch (path.mAttributeId) { case WaterHeaterManagement::Attributes::HeaterTypes::Id: { - chip::BitMask value; + chip::BitMask value; ReturnErrorOnFailure(chip::app::DataModel::Decode(*data, value)); return DataModelLogger::LogValue("HeaterTypes", 1, value); } case WaterHeaterManagement::Attributes::HeatDemand::Id: { - chip::BitMask value; + chip::BitMask value; ReturnErrorOnFailure(chip::app::DataModel::Decode(*data, value)); return DataModelLogger::LogValue("HeatDemand", 1, value); } @@ -20629,6 +20711,22 @@ CHIP_ERROR DataModelLogger::LogEvent(const chip::app::EventHeader & header, chip } break; } + case WaterHeaterManagement::Id: { + switch (header.mPath.mEventId) + { + case WaterHeaterManagement::Events::BoostStarted::Id: { + chip::app::Clusters::WaterHeaterManagement::Events::BoostStarted::DecodableType value; + ReturnErrorOnFailure(chip::app::DataModel::Decode(*data, value)); + return DataModelLogger::LogValue("BoostStarted", 1, value); + } + case WaterHeaterManagement::Events::BoostEnded::Id: { + chip::app::Clusters::WaterHeaterManagement::Events::BoostEnded::DecodableType value; + ReturnErrorOnFailure(chip::app::DataModel::Decode(*data, value)); + return DataModelLogger::LogValue("BoostEnded", 1, value); + } + } + break; + } case DemandResponseLoadControl::Id: { switch (header.mPath.mEventId) { diff --git a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.h b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.h index 60398d0f3d2911..5646abd25a011a 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.h +++ b/zzz_generated/chip-tool/zap-generated/cluster/logging/DataModelLogger.h @@ -214,6 +214,10 @@ static CHIP_ERROR LogValue(const char * label, size_t indent, const chip::app::Clusters::ElectricalEnergyMeasurement::Structs::EnergyMeasurementStruct::DecodableType & value); +static CHIP_ERROR +LogValue(const char * label, size_t indent, + const chip::app::Clusters::WaterHeaterManagement::Structs::WaterHeaterBoostInfoStruct::DecodableType & value); + static CHIP_ERROR LogValue(const char * label, size_t indent, const chip::app::Clusters::DemandResponseLoadControl::Structs::HeatingSourceControlStruct::DecodableType & value); @@ -574,6 +578,10 @@ LogValue(const char * label, size_t indent, static CHIP_ERROR LogValue(const char * label, size_t indent, const chip::app::Clusters::ElectricalEnergyMeasurement::Events::PeriodicEnergyMeasured::DecodableType & value); +static CHIP_ERROR LogValue(const char * label, size_t indent, + const chip::app::Clusters::WaterHeaterManagement::Events::BoostStarted::DecodableType & value); +static CHIP_ERROR LogValue(const char * label, size_t indent, + const chip::app::Clusters::WaterHeaterManagement::Events::BoostEnded::DecodableType & value); static CHIP_ERROR LogValue(const char * label, size_t indent, const chip::app::Clusters::DemandResponseLoadControl::Events::LoadControlEventStatusChange::DecodableType & value); diff --git a/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h b/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h index 67f8781db8fd5d..a61f2a6fa6b27e 100644 --- a/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h +++ b/zzz_generated/darwin-framework-tool/zap-generated/cluster/Commands.h @@ -79923,6 +79923,8 @@ class SubscribeAttributeElectricalEnergyMeasurementClusterRevision : public Subs | * ClusterRevision | 0xFFFD | |------------------------------------------------------------------------------| | Events: | | +| * BoostStarted | 0x0000 | +| * BoostEnded | 0x0001 | \*----------------------------------------------------------------------------*/ #if MTR_ENABLE_PROVISIONAL @@ -79933,24 +79935,10 @@ class WaterHeaterManagementBoost : public ClusterCommand { public: WaterHeaterManagementBoost() : ClusterCommand("boost") + , mComplex_BoostInfo(&mRequest.boostInfo) { #if MTR_ENABLE_PROVISIONAL - AddArgument("Duration", 0, UINT32_MAX, &mRequest.duration); -#endif // MTR_ENABLE_PROVISIONAL -#if MTR_ENABLE_PROVISIONAL - AddArgument("OneShot", 0, 1, &mRequest.oneShot); -#endif // MTR_ENABLE_PROVISIONAL -#if MTR_ENABLE_PROVISIONAL - AddArgument("EmergencyBoost", 0, 1, &mRequest.emergencyBoost); -#endif // MTR_ENABLE_PROVISIONAL -#if MTR_ENABLE_PROVISIONAL - AddArgument("TemporarySetpoint", INT16_MIN, INT16_MAX, &mRequest.temporarySetpoint); -#endif // MTR_ENABLE_PROVISIONAL -#if MTR_ENABLE_PROVISIONAL - AddArgument("TargetPercentage", 0, UINT8_MAX, &mRequest.targetPercentage); -#endif // MTR_ENABLE_PROVISIONAL -#if MTR_ENABLE_PROVISIONAL - AddArgument("TargetReheat", 0, UINT8_MAX, &mRequest.targetReheat); + AddArgument("BoostInfo", &mComplex_BoostInfo); #endif // MTR_ENABLE_PROVISIONAL ClusterCommand::AddArguments(); } @@ -79967,41 +79955,32 @@ class WaterHeaterManagementBoost : public ClusterCommand { __auto_type * params = [[MTRWaterHeaterManagementClusterBoostParams alloc] init]; params.timedInvokeTimeoutMs = mTimedInteractionTimeoutMs.HasValue() ? [NSNumber numberWithUnsignedShort:mTimedInteractionTimeoutMs.Value()] : nil; #if MTR_ENABLE_PROVISIONAL - params.duration = [NSNumber numberWithUnsignedInt:mRequest.duration]; -#endif // MTR_ENABLE_PROVISIONAL -#if MTR_ENABLE_PROVISIONAL - if (mRequest.oneShot.HasValue()) { - params.oneShot = [NSNumber numberWithBool:mRequest.oneShot.Value()]; + params.boostInfo = [MTRWaterHeaterManagementClusterWaterHeaterBoostInfoStruct new]; + params.boostInfo.duration = [NSNumber numberWithUnsignedInt:mRequest.boostInfo.duration]; + if (mRequest.boostInfo.oneShot.HasValue()) { + params.boostInfo.oneShot = [NSNumber numberWithBool:mRequest.boostInfo.oneShot.Value()]; } else { - params.oneShot = nil; + params.boostInfo.oneShot = nil; } -#endif // MTR_ENABLE_PROVISIONAL -#if MTR_ENABLE_PROVISIONAL - if (mRequest.emergencyBoost.HasValue()) { - params.emergencyBoost = [NSNumber numberWithBool:mRequest.emergencyBoost.Value()]; + if (mRequest.boostInfo.emergencyBoost.HasValue()) { + params.boostInfo.emergencyBoost = [NSNumber numberWithBool:mRequest.boostInfo.emergencyBoost.Value()]; } else { - params.emergencyBoost = nil; + params.boostInfo.emergencyBoost = nil; } -#endif // MTR_ENABLE_PROVISIONAL -#if MTR_ENABLE_PROVISIONAL - if (mRequest.temporarySetpoint.HasValue()) { - params.temporarySetpoint = [NSNumber numberWithShort:mRequest.temporarySetpoint.Value()]; + if (mRequest.boostInfo.temporarySetpoint.HasValue()) { + params.boostInfo.temporarySetpoint = [NSNumber numberWithShort:mRequest.boostInfo.temporarySetpoint.Value()]; } else { - params.temporarySetpoint = nil; + params.boostInfo.temporarySetpoint = nil; } -#endif // MTR_ENABLE_PROVISIONAL -#if MTR_ENABLE_PROVISIONAL - if (mRequest.targetPercentage.HasValue()) { - params.targetPercentage = [NSNumber numberWithUnsignedChar:mRequest.targetPercentage.Value()]; + if (mRequest.boostInfo.targetPercentage.HasValue()) { + params.boostInfo.targetPercentage = [NSNumber numberWithUnsignedChar:mRequest.boostInfo.targetPercentage.Value()]; } else { - params.targetPercentage = nil; + params.boostInfo.targetPercentage = nil; } -#endif // MTR_ENABLE_PROVISIONAL -#if MTR_ENABLE_PROVISIONAL - if (mRequest.targetReheat.HasValue()) { - params.targetReheat = [NSNumber numberWithUnsignedChar:mRequest.targetReheat.Value()]; + if (mRequest.boostInfo.targetReheat.HasValue()) { + params.boostInfo.targetReheat = [NSNumber numberWithUnsignedChar:mRequest.boostInfo.targetReheat.Value()]; } else { - params.targetReheat = nil; + params.boostInfo.targetReheat = nil; } #endif // MTR_ENABLE_PROVISIONAL uint16_t repeatCount = mRepeatCount.ValueOr(1); @@ -80025,6 +80004,7 @@ class WaterHeaterManagementBoost : public ClusterCommand { private: chip::app::Clusters::WaterHeaterManagement::Commands::Boost::Type mRequest; + TypedComplexArgument mComplex_BoostInfo; }; #endif // MTR_ENABLE_PROVISIONAL @@ -195156,6 +195136,8 @@ void registerClusterWaterHeaterManagement(Commands & commands) make_unique(), // make_unique(), // #endif // MTR_ENABLE_PROVISIONAL + make_unique(Id), // + make_unique(Id), // }; commands.RegisterCluster(clusterName, clusterCommands);