diff --git a/docs/guides/fabric_synchronization_guide.md b/docs/guides/fabric_synchronization_guide.md new file mode 100644 index 00000000000000..36107744a97930 --- /dev/null +++ b/docs/guides/fabric_synchronization_guide.md @@ -0,0 +1,164 @@ +# Fabric Synchronization Guide + +- [Fabric Synchronization Guide](#fabric-synchronization-guide) + - [Fabric Sync Example Applications](#fabric-sync-example-applications) + - [Run Fabric Sync Demo on RP4](#run-fabric-sync-demo-on-rp4) + +## Fabric Sync Example Applications + +Fabric-Admin and Fabric-Bridge example applications are provided to demonstrate +Fabric Synchronization feature. You can find them in the examples. + +![matter_fabric_synchronization](images/matter_fabric_synchronization.png) + +Fabric-Admin example app implements the Fabric Administrator role and +communicates with the Fabric-Bridge-App on the other side, facilitating the +Fabric Synchronization process. + +Fabric-Bridge-App example app implements the Aggregator device type with Fabric +Synchronization condition met and demonstrates the end-to-end Fabric +Synchronization feature using dynamic endpoints. + +Fabric Synchronization can be triggered from either side. The initiator of the +Fabric Synchronization process, who shares their devices, takes on the +Commissioner role. The recipient of the Fabric Synchronization request, who +receives the shared devices, assumes the Commissionee role. This flexibility +enables a seamless and efficient synchronization process. + +### Building the Example Application + +- Building the Fabric-Admin Application + + [Fabric-Admin](https://github.com/project-chip/connectedhomeip/tree/master/examples/fabric-admin/README.md) + +* Building the Fabric-Bridge Application + + [Fabric-Bridge](https://github.com/project-chip/connectedhomeip/tree/master/examples/fabric-bridge-app/linux/README.md) + +## Run Fabric Sync Demo on RP4 + +### Setup Fabric Source + +Connect to the Fabric Source server: + +``` +ssh ubuntu@xxx.xxx.xxx.xxx +``` + +Password: + +Run the Fabric Source script: + +``` +./run_fabric_source.sh +``` + +### Setup Fabric Sink + +Connect to the Fabric Sink server: + +``` +ssh ubuntu@xxx.xxx.xxx.xxx +``` + +Password: + +Run the Fabric Sink script: + +``` +./run_fabric_sink.sh +``` + +### Fabric Sync Setup + +Enable Fabric Auto Sync: + +In Fabric-Sync console: + +``` +fabricsync enable-auto-sync 1 +``` + +Pair the Fabric-Source bridge to Fabric-Sync with node ID 1: + +``` +fabricsync add-bridge 1 +``` + +### Pair Light Example to Fabric-Source + +Pair the Light Example with node ID 3 using its payload number: + +``` +pairing already-discovered 3 20202021 5540 +``` + +After the Light Example is successfully paired in Fabric-Source, it will be +synced to Fabric-Sink with a new assigned node ID. + +Toggle the Light Example: + +From Fabric-Source: + +``` +onoff on 1 +onoff off 1 +``` + +From Fabric-Sink: (Use the node ID assigned) + +``` +onoff on x 1 +onoff off x 1 +``` + +### Remove Light Example from Fabric-Source + +Unpair the Light Example: + +``` +pairing unpair +``` + +After the Light Example is successfully unpaired from Fabric-Source, it will +also be removed from the Fabric-Sink. + +### Pair Commercial Switch to Fabric-Source + +Pair the switch using its payload number: + +In Fabric-Source console: + +``` +pairing code-wifi +``` + +After the switch is successfully paired in Fabric-Source, it will be synced to +Fabric-Sink with a new assigned node ID. + +Toggle the switch: + +From Fabric-Source: + +``` +onoff on 1 +onoff off 1 +``` + +From Fabric-Sink: (Use the node ID assigned) + +``` +onoff on 1 +onoff off 1 +``` + +### Remove Switch from Fabric-Source + +Unpair the switch: + +``` +pairing unpair +``` + +After the switch is successfully unpaired from Fabric-Source, it will also be +removed from the Fabric-Sink. diff --git a/docs/guides/images/matter_fabric_synchronization.png b/docs/guides/images/matter_fabric_synchronization.png new file mode 100644 index 00000000000000..95c99c4fe150b9 Binary files /dev/null and b/docs/guides/images/matter_fabric_synchronization.png differ diff --git a/examples/fabric-admin/scripts/run_fabric_sink.sh b/examples/fabric-admin/scripts/run_fabric_sink.sh new file mode 100755 index 00000000000000..3013965479268a --- /dev/null +++ b/examples/fabric-admin/scripts/run_fabric_sink.sh @@ -0,0 +1,78 @@ +#!/bin/bash + +# Default paths +DEFAULT_CHOICES=( + "./fabric-admin" + "out/debug/standalone/fabric-admin" + "out/linux-x64-fabric-admin/fabric-admin" + "out/darwin-arm64-fabric-admin/fabric-admin" +) +FABRIC_ADMIN_LOG="/tmp/fabric_admin.log" +FABRIC_ADMIN_PATH="" + +# Function to find fabric-admin binary +find_fabric_admin() { + local choices=("$@") + for path in "${choices[@]}"; do + if [[ -e "$path" ]]; then + echo "$path" + return 0 + fi + done + return 1 +} + +# Parse arguments +VERBOSE=false +SPECIFIED_PATH="" + +for arg in "$@"; do + case $arg in + --verbose) + VERBOSE=true + ;; + --path=*) + SPECIFIED_PATH="${arg#*=}" + ;; + esac +done + +# Use specified path if provided +if [[ -n "$SPECIFIED_PATH" ]]; then + if [[ -e "$SPECIFIED_PATH" ]]; then + FABRIC_ADMIN_PATH="$SPECIFIED_PATH" + else + echo >&2 "Specified path does not exist: $SPECIFIED_PATH" + exit 1 + fi +else + FABRIC_ADMIN_PATH=$(find_fabric_admin "${DEFAULT_CHOICES[@]}") + if [[ $? -ne 0 ]]; then + echo >&2 "Could not find the fabric-admin binary" + exit 1 + fi +fi + +echo "PATH IS: $FABRIC_ADMIN_PATH" + +# Kill fabric-admin if it is running +echo "Checking for running fabric-admin process..." +fabric_admin_pid=$(pgrep -f "$FABRIC_ADMIN_PATH") +if [[ -n "$fabric_admin_pid" ]]; then + echo "Found fabric-admin with PID $fabric_admin_pid, attempting to kill..." + kill -9 "$fabric_admin_pid" + echo "Killed fabric-admin with PID $fabric_admin_pid" +fi + +# Remove /tmp/chip_* files and directories +echo "Removing /tmp/chip_* files and directories..." +sudo rm -rf /tmp/chip_* +echo "Removed /tmp/chip_* files and directories" + +# Start fabric-admin with or without log file path based on --verbose option +echo "Starting fabric-admin..." +if [ "$VERBOSE" = true ]; then + "$FABRIC_ADMIN_PATH" +else + "$FABRIC_ADMIN_PATH" --log-file-path "$FABRIC_ADMIN_LOG" +fi diff --git a/examples/fabric-admin/scripts/run_fabric_source.sh b/examples/fabric-admin/scripts/run_fabric_source.sh new file mode 100755 index 00000000000000..95df7a135bb596 --- /dev/null +++ b/examples/fabric-admin/scripts/run_fabric_source.sh @@ -0,0 +1,116 @@ +#!/bin/bash + +# Get the path to the current script +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Default paths +DEFAULT_ADMIN_CHOICES=( + "./fabric-admin" + "out/debug/standalone/fabric-admin" + "out/linux-x64-fabric-admin/fabric-admin" + "out/darwin-arm64-fabric-admin/fabric-admin" +) +DEFAULT_BRIDGE_CHOICES=( + "./fabric-bridge-app" + "out/debug/standalone/fabric-bridge-app" + "out/linux-x64-fabric-bridge-app/fabric-bridge-app" + "out/darwin-arm64-fabric-bridge-app/fabric-bridge-app" +) +FABRIC_ADMIN_LOG="/tmp/fabric_admin.log" +FABRIC_BRIDGE_APP_LOG="/tmp/fabric_bridge_app.log" +FABRIC_ADMIN_PATH="" +FABRIC_BRIDGE_APP_PATH="" + +# Function to find a binary +find_binary() { + local choices=("$@") + for path in "${choices[@]}"; do + if [[ -e "$path" ]]; then + echo "$path" + return 0 + fi + done + return 1 +} + +# Parse arguments +VERBOSE=false +SPECIFIED_ADMIN_PATH="" +SPECIFIED_BRIDGE_PATH="" + +for arg in "$@"; do + case $arg in + --verbose) + VERBOSE=true + ;; + --admin-path=*) + SPECIFIED_ADMIN_PATH="${arg#*=}" + ;; + --bridge-path=*) + SPECIFIED_BRIDGE_PATH="${arg#*=}" + ;; + esac +done + +# Use specified paths if provided +if [[ -n "$SPECIFIED_ADMIN_PATH" ]]; then + if [[ -e "$SPECIFIED_ADMIN_PATH" ]]; then + FABRIC_ADMIN_PATH="$SPECIFIED_ADMIN_PATH" + else + echo >&2 "Specified admin path does not exist: $SPECIFIED_ADMIN_PATH" + exit 1 + fi +else + FABRIC_ADMIN_PATH=$(find_binary "${DEFAULT_ADMIN_CHOICES[@]}") + if [[ $? -ne 0 ]]; then + echo >&2 "Could not find the fabric-admin binary" + exit 1 + fi +fi + +if [[ -n "$SPECIFIED_BRIDGE_PATH" ]]; then + if [[ -e "$SPECIFIED_BRIDGE_PATH" ]]; then + FABRIC_BRIDGE_APP_PATH="$SPECIFIED_BRIDGE_PATH" + else + echo >&2 "Specified bridge path does not exist: $SPECIFIED_BRIDGE_PATH" + exit 1 + fi +else + FABRIC_BRIDGE_APP_PATH=$(find_binary "${DEFAULT_BRIDGE_CHOICES[@]}") + if [[ $? -ne 0 ]]; then + echo >&2 "Could not find the fabric-bridge-app binary" + exit 1 + fi +fi + +echo "Admin path: $FABRIC_ADMIN_PATH" +echo "Bridge path: $FABRIC_BRIDGE_APP_PATH" + +# Determine the path to stop_fabric_source.sh based on the location of run_fabric_source.sh +RUN_FABRIC_SOURCE_PATH=$(find_binary "$SCRIPT_DIR/run_fabric_source.sh") +if [[ $? -ne 0 ]]; then + echo >&2 "Could not find the run_fabric_source.sh script" + exit 1 +fi +STOP_FABRIC_SOURCE_PATH="${RUN_FABRIC_SOURCE_PATH/run_fabric_source/stop_fabric_source}" + +# Stop any running instances and clean up +if [[ -e "$STOP_FABRIC_SOURCE_PATH" ]]; then + "$STOP_FABRIC_SOURCE_PATH" +else + echo >&2 "Could not find the stop_fabric_source.sh script" + exit 1 +fi + +# Start fabric-bridge-app if available and redirect its output to /dev/null +if [ -f "$FABRIC_BRIDGE_APP_PATH" ]; then + "$FABRIC_BRIDGE_APP_PATH" >"$FABRIC_BRIDGE_APP_LOG" 2>&1 & + echo "Started fabric-bridge-app" +fi + +# Start fabric-admin with or without log file path based on --verbose option +if [ "$VERBOSE" = true ]; then + "$FABRIC_ADMIN_PATH" +else + "$FABRIC_ADMIN_PATH" --log-file-path "$FABRIC_ADMIN_LOG" +fi diff --git a/examples/fabric-admin/scripts/stop_fabric_source.sh b/examples/fabric-admin/scripts/stop_fabric_source.sh new file mode 100755 index 00000000000000..85fff9e3a878cc --- /dev/null +++ b/examples/fabric-admin/scripts/stop_fabric_source.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +FABRIC_ADMIN_PATH="/fabric-admin" +FABRIC_BRIDGE_APP_PATH="/fabric-bridge-app" + +# Kill fabric-admin if it is running +fabric_admin_pid=$(pgrep -f "$FABRIC_ADMIN_PATH") +if [ ! -z "$fabric_admin_pid" ]; then + kill -9 "$fabric_admin_pid" + echo "Killed fabric-admin with PID $fabric_admin_pid" +fi + +# Kill fabric-bridge-app if it is running +fabric_bridge_app_pid=$(pgrep -f "$FABRIC_BRIDGE_APP_PATH") +if [ ! -z "$fabric_bridge_app_pid" ]; then + kill -9 "$fabric_bridge_app_pid" + echo "Killed fabric-bridge-app with PID $fabric_bridge_app_pid" +fi + +# Remove /tmp/chip_* files and directories +sudo rm -rf /tmp/chip_* +echo "Removed /tmp/chip_* files and directories" diff --git a/src/app/clusters/door-lock-server/door-lock-server.cpp b/src/app/clusters/door-lock-server/door-lock-server.cpp index 481587322216eb..4ab8744b4dbd32 100644 --- a/src/app/clusters/door-lock-server/door-lock-server.cpp +++ b/src/app/clusters/door-lock-server/door-lock-server.cpp @@ -51,39 +51,6 @@ static constexpr uint8_t DOOR_LOCK_ALIRO_CREDENTIAL_SIZE = 65; static constexpr uint32_t DOOR_LOCK_MAX_LOCK_TIMEOUT_SEC = MAX_INT32U_VALUE / MILLISECOND_TICKS_PER_SECOND; -static constexpr size_t kDoorLockDelegateTableSize = - MATTER_DM_DOOR_LOCK_CLUSTER_SERVER_ENDPOINT_COUNT + CHIP_DEVICE_CONFIG_DYNAMIC_ENDPOINT_COUNT; - -static_assert(kDoorLockDelegateTableSize <= kEmberInvalidEndpointIndex, "Door Lock Delegate table size error"); - -namespace chip { -namespace app { -namespace Clusters { -namespace DoorLock { - -Delegate * gDelegateTable[kDoorLockDelegateTableSize] = { nullptr }; - -Delegate * GetDelegate(EndpointId endpoint) -{ - uint16_t ep = emberAfGetClusterServerEndpointIndex(endpoint, DoorLock::Id, MATTER_DM_DOOR_LOCK_CLUSTER_SERVER_ENDPOINT_COUNT); - return (ep >= kDoorLockDelegateTableSize ? nullptr : gDelegateTable[ep]); -} - -void SetDefaultDelegate(EndpointId endpoint, Delegate * delegate) -{ - uint16_t ep = emberAfGetClusterServerEndpointIndex(endpoint, DoorLock::Id, MATTER_DM_DOOR_LOCK_CLUSTER_SERVER_ENDPOINT_COUNT); - // if endpoint is found - if (ep < ArraySize(gDelegateTable)) - { - gDelegateTable[ep] = delegate; - } -} - -} // namespace DoorLock -} // namespace Clusters -} // namespace app -} // namespace chip - DoorLockServer DoorLockServer::instance; class DoorLockClusterFabricDelegate : public chip::FabricTable::Delegate @@ -117,7 +84,18 @@ DoorLockServer & DoorLockServer::Instance() * * @param endpointId */ -void DoorLockServer::InitServer(chip::EndpointId endpointId) +void DoorLockServer::InitServer(EndpointId endpointId) +{ + CHIP_ERROR err = InitEndpoint(endpointId); + + // We have no way to communicate this error, so just log it. + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "Door Lock cluster initialization on endpoint %d failed: %" CHIP_ERROR_FORMAT, endpointId, err.Format()); + } +} + +CHIP_ERROR DoorLockServer::InitEndpoint(EndpointId endpointId, Delegate * delegate) { ChipLogProgress(Zcl, "Door Lock cluster initialized at endpoint #%u", endpointId); @@ -128,11 +106,48 @@ void DoorLockServer::InitServer(chip::EndpointId endpointId) } SetActuatorEnabled(endpointId, true); - for (auto & ep : mEndpointCtx) + auto * endpointContext = getContext(endpointId); + if (!endpointContext) + { + ChipLogError(Zcl, "Invalid endpoint %d for initializing lock server: no endpoint context available", endpointId); + return CHIP_ERROR_INVALID_ARGUMENT; + } + + endpointContext->lockoutEndTimestamp = endpointContext->lockoutEndTimestamp.zero(); + endpointContext->wrongCodeEntryAttempts = 0; + endpointContext->delegate = delegate; + return CHIP_NO_ERROR; +} + +void DoorLockServer::ShutdownEndpoint(EndpointId endpointId) +{ + auto * endpointContext = getContext(endpointId); + if (!endpointContext) { - ep.lockoutEndTimestamp = ep.lockoutEndTimestamp.zero(); - ep.wrongCodeEntryAttempts = 0; + ChipLogError(Zcl, "Invalid endpoint %d for shutting down lock server: no endpoint context available", endpointId); + return; } + + endpointContext->delegate = nullptr; +} + +CHIP_ERROR DoorLockServer::SetDelegate(chip::EndpointId endpointId, chip::app::Clusters::DoorLock::Delegate * delegate) +{ + if (!delegate) + { + ChipLogError(Zcl, "Trying to set a null DoorLock::Delegate on endpoint %d", endpointId); + return CHIP_ERROR_INVALID_ARGUMENT; + } + + auto * endpointContext = getContext(endpointId); + if (!endpointContext) + { + ChipLogError(Zcl, "Invalid endpoint %d for setting a delegate: no endpoint context available", endpointId); + return CHIP_ERROR_INVALID_ARGUMENT; + } + + endpointContext->delegate = delegate; + return CHIP_NO_ERROR; } bool DoorLockServer::SetLockState(chip::EndpointId endpointId, DlLockState newLockState) @@ -3445,6 +3460,17 @@ EmberAfDoorLockEndpointContext * DoorLockServer::getContext(chip::EndpointId end return nullptr; } +Delegate * DoorLockServer::GetDelegate(EndpointId endpointId) +{ + auto * endpointContext = getContext(endpointId); + if (!endpointContext) + { + return nullptr; + } + + return endpointContext->delegate; +} + bool DoorLockServer::HandleRemoteLockOperation(chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, LockOperationTypeEnum opType, RemoteLockOpHandler opHandler, const Optional & pinCode) diff --git a/src/app/clusters/door-lock-server/door-lock-server.h b/src/app/clusters/door-lock-server/door-lock-server.h index bf2798115c985c..ccb62dde46712b 100644 --- a/src/app/clusters/door-lock-server/door-lock-server.h +++ b/src/app/clusters/door-lock-server/door-lock-server.h @@ -86,20 +86,9 @@ struct EmberAfDoorLockEndpointContext { chip::System::Clock::Timestamp lockoutEndTimestamp; int wrongCodeEntryAttempts; + chip::app::Clusters::DoorLock::Delegate * delegate = nullptr; }; -namespace chip { -namespace app { -namespace Clusters { -namespace DoorLock { - -void SetDefaultDelegate(EndpointId endpoint, Delegate * delegate); - -} // namespace DoorLock -} // namespace Clusters -} // namespace app -} // namespace chip - /** * @brief Door Lock Server Plugin class. */ @@ -112,7 +101,28 @@ class DoorLockServer : public chip::app::AttributeAccessInterface using Feature = chip::app::Clusters::DoorLock::Feature; using OnFabricRemovedCustomCallback = void (*)(chip::EndpointId endpointId, chip::FabricIndex fabricIndex); - void InitServer(chip::EndpointId endpointId); + /** + * Multiple InitEndpoint calls can happen for different endpoints. Calling + * InitEndpoint twice for the same endpoint requires a ShutdownEndpoint call + * for that endpoint in between. + * + * A DoorLock::Delegate is optional, but needs to be provided in either + * InitEndpoint or in a separate SetDelegate call for Aliro features, and + * possibly other new features, to work. + */ + CHIP_ERROR InitEndpoint(chip::EndpointId endpointId, chip::app::Clusters::DoorLock::Delegate * delegate = nullptr); + + void ShutdownEndpoint(chip::EndpointId endpointId); + + // InitServer is a deprecated alias for InitEndpoint with no delegate. + void InitServer(chip::EndpointId endpointid); + + /** + * Delegate is not supposed to be null. Removing a delegate + * should only happen when shutting down the door lock cluster on the + * endpoint, via ShutdownEndpoint. + */ + CHIP_ERROR SetDelegate(chip::EndpointId endpointId, chip::app::Clusters::DoorLock::Delegate * delegate); /** * Updates the LockState attribute with new value and sends LockOperation event. @@ -488,6 +498,13 @@ class DoorLockServer : public chip::app::AttributeAccessInterface static void sendClusterResponse(chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, chip::Protocols::InteractionModel::ClusterStatusCode status); + /** + * Get the DoorLock::Delegate for the given endpoint, if any. Will return + * null if there is no door lock server initialized on that endpoint or if + * there is no delegate associated with the initialized server. + */ + chip::app::Clusters::DoorLock::Delegate * GetDelegate(chip::EndpointId endpointId); + /** * @brief Common handler for LockDoor, UnlockDoor, UnlockWithTimeout commands * diff --git a/src/app/clusters/time-synchronization-server/time-synchronization-server.h b/src/app/clusters/time-synchronization-server/time-synchronization-server.h index 2581c9712e7862..ecc9a68cc3fc12 100644 --- a/src/app/clusters/time-synchronization-server/time-synchronization-server.h +++ b/src/app/clusters/time-synchronization-server/time-synchronization-server.h @@ -124,9 +124,10 @@ class TimeSynchronizationServer : public FabricTable::Delegate void OnAttributeData(const ConcreteDataAttributePath & aPath, TLV::TLVReader * apData, const StatusIB & aStatus) override; void OnDone(ReadClient * apReadClient) override; - CHIP_ERROR AttemptToGetTimeFromTrustedNode(); #endif + CHIP_ERROR AttemptToGetTimeFromTrustedNode(); + // Platform event handler functions void OnPlatformEventFn(const DeviceLayer::ChipDeviceEvent & event); diff --git a/src/darwin/Framework/CHIP/MTRDevice.h b/src/darwin/Framework/CHIP/MTRDevice.h index a8098a7174572e..1c31cbc26a661b 100644 --- a/src/darwin/Framework/CHIP/MTRDevice.h +++ b/src/darwin/Framework/CHIP/MTRDevice.h @@ -114,7 +114,7 @@ MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) * * The delegate will be called on the provided queue, for attribute reports, event reports, and device state changes. */ -- (void)setDelegate:(id)delegate queue:(dispatch_queue_t)queue MTR_NEWLY_DEPRECATED("Please use addDelegate:queue:interestedPaths:"); +- (void)setDelegate:(id)delegate queue:(dispatch_queue_t)queue MTR_DEPRECATED("Please use addDelegate:queue:interestedPaths:", ios(16.1, 18.0), macos(13.0, 15.0), watchos(9.1, 11.0), tvos(16.1, 18.0)); /** * Adds a delegate to receive asynchronous callbacks about the device. @@ -123,7 +123,7 @@ MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) * * MTRDevice holds a weak reference to the delegate object. */ -- (void)addDelegate:(id)delegate queue:(dispatch_queue_t)queue MTR_NEWLY_AVAILABLE; +- (void)addDelegate:(id)delegate queue:(dispatch_queue_t)queue MTR_AVAILABLE(ios(18.0), macos(15.0), watchos(11.0), tvos(18.0)); /** * Adds a delegate to receive asynchronous callbacks about the device, and limit attribute and/or event reports to a specific set of paths. @@ -138,12 +138,12 @@ MTR_AVAILABLE(ios(16.1), macos(13.0), watchos(9.1), tvos(16.1)) * * MTRDevice holds a weak reference to the delegate object. */ -- (void)addDelegate:(id)delegate queue:(dispatch_queue_t)queue interestedPathsForAttributes:(NSArray * _Nullable)interestedPathsForAttributes interestedPathsForEvents:(NSArray * _Nullable)interestedPathsForEvents MTR_NEWLY_AVAILABLE; +- (void)addDelegate:(id)delegate queue:(dispatch_queue_t)queue interestedPathsForAttributes:(NSArray * _Nullable)interestedPathsForAttributes interestedPathsForEvents:(NSArray * _Nullable)interestedPathsForEvents MTR_AVAILABLE(ios(18.0), macos(15.0), watchos(11.0), tvos(18.0)); /** * Removes the delegate from receiving callbacks about the device. */ -- (void)removeDelegate:(id)delegate MTR_NEWLY_AVAILABLE; +- (void)removeDelegate:(id)delegate MTR_AVAILABLE(ios(18.0), macos(15.0), watchos(11.0), tvos(18.0)); /** * Read attribute in a designated attribute path. If there is no value available diff --git a/src/darwin/Framework/CHIP/MTRDevice.mm b/src/darwin/Framework/CHIP/MTRDevice.mm index 89478554cbdc8b..298b31a5406bce 100644 --- a/src/darwin/Framework/CHIP/MTRDevice.mm +++ b/src/darwin/Framework/CHIP/MTRDevice.mm @@ -802,7 +802,7 @@ - (void)addDelegate:(id)delegate queue:(dispatch_queue_t)queu [self _addDelegate:delegate queue:queue interestedPathsForAttributes:nil interestedPathsForEvents:nil]; } -- (void)addDelegate:(id)delegate queue:(dispatch_queue_t)queue interestedPathsForAttributes:(NSArray * _Nullable)interestedPathsForAttributes interestedPathsForEvents:(NSArray * _Nullable)interestedPathsForEvents MTR_NEWLY_AVAILABLE; +- (void)addDelegate:(id)delegate queue:(dispatch_queue_t)queue interestedPathsForAttributes:(NSArray * _Nullable)interestedPathsForAttributes interestedPathsForEvents:(NSArray * _Nullable)interestedPathsForEvents { MTR_LOG("%@ addDelegate %@ with interested attribute paths %@ event paths %@", self, delegate, interestedPathsForAttributes, interestedPathsForEvents); [self _addDelegate:delegate queue:queue interestedPathsForAttributes:interestedPathsForAttributes interestedPathsForEvents:interestedPathsForEvents]; diff --git a/src/darwin/Framework/CHIP/templates/availability.yaml b/src/darwin/Framework/CHIP/templates/availability.yaml index 88241256e2eca2..5d8aef90a50d95 100644 --- a/src/darwin/Framework/CHIP/templates/availability.yaml +++ b/src/darwin/Framework/CHIP/templates/availability.yaml @@ -9689,10 +9689,12 @@ versions: "future" provisional: clusters: - # Targeting Fall 2024 + # Targeting 1.4 + - CommissionerControl - ServiceArea - ThreadBorderRouterManagement - ThreadNetworkDirectory + - WaterHeaterManagement - WiFiNetworkManagement attributes: OccupancySensing: @@ -9711,10 +9713,10 @@ - HoldTimeLimitsStruct bitmaps: OccupancySensing: - # Targeting Fall 1.4 + # Targeting 1.4 - Feature bitmap values: Switch: Feature: - # Targeting Fall 2024 + # Targeting 1.4 - ActionSwitch