diff --git a/examples/lock-app/lock-common/include/LockEndpoint.h b/examples/lock-app/lock-common/include/LockEndpoint.h index 6e1fb0311f29b4..63af4f46b0de30 100644 --- a/examples/lock-app/lock-common/include/LockEndpoint.h +++ b/examples/lock-app/lock-common/include/LockEndpoint.h @@ -64,9 +64,12 @@ class LockEndpoint inline chip::EndpointId GetEndpointId() const { return mEndpointId; } - bool Lock(const Optional & pin, OperationErrorEnum & err, OperationSourceEnum opSource); - bool Unlock(const Optional & pin, OperationErrorEnum & err, OperationSourceEnum opSource); - bool Unbolt(const Optional & pin, OperationErrorEnum & err, OperationSourceEnum opSource); + bool Lock(const Nullable & fabricIdx, const Nullable & nodeId, + const Optional & pin, OperationErrorEnum & err, OperationSourceEnum opSource); + bool Unlock(const Nullable & fabricIdx, const Nullable & nodeId, + const Optional & pin, OperationErrorEnum & err, OperationSourceEnum opSource); + bool Unbolt(const Nullable & fabricIdx, const Nullable & nodeId, + const Optional & pin, OperationErrorEnum & err, OperationSourceEnum opSource); bool GetUser(uint16_t userIndex, EmberAfPluginDoorLockUserInfo & user) const; bool SetUser(uint16_t userIndex, chip::FabricIndex creator, chip::FabricIndex modifier, const chip::CharSpan & userName, @@ -98,13 +101,16 @@ class LockEndpoint OperatingModeEnum operatingMode); private: - bool setLockState(DlLockState lockState, const Optional & pin, OperationErrorEnum & err, + bool setLockState(const Nullable & fabricIdx, const Nullable & nodeId, DlLockState lockState, + const Optional & pin, OperationErrorEnum & err, OperationSourceEnum opSource = OperationSourceEnum::kUnspecified); const char * lockStateToString(DlLockState lockState) const; bool weekDayScheduleInAction(uint16_t userIndex) const; bool yearDayScheduleInAction(uint16_t userIndex) const; + static void OnLockActionCompleteCallback(chip::System::Layer *, void * callbackContext); + chip::EndpointId mEndpointId; DlLockState mLockState; DoorStateEnum mDoorState; diff --git a/examples/lock-app/lock-common/include/LockManager.h b/examples/lock-app/lock-common/include/LockManager.h index eb6d6e99d51e66..eea60d568ef06b 100644 --- a/examples/lock-app/lock-common/include/LockManager.h +++ b/examples/lock-app/lock-common/include/LockManager.h @@ -35,12 +35,12 @@ class LockManager bool SendLockAlarm(chip::EndpointId endpointId, AlarmCodeEnum alarmCode); - bool Lock(chip::EndpointId endpointId, const Optional & pin, OperationErrorEnum & err, - OperationSourceEnum opSource); - bool Unlock(chip::EndpointId endpointId, const Optional & pin, OperationErrorEnum & err, - OperationSourceEnum opSource); - bool Unbolt(chip::EndpointId endpointId, const Optional & pin, OperationErrorEnum & err, - OperationSourceEnum opSource); + bool Lock(chip::EndpointId endpointId, const Nullable & fabricIdx, const Nullable & nodeId, + const Optional & pin, OperationErrorEnum & err, OperationSourceEnum opSource); + bool Unlock(chip::EndpointId endpointId, const Nullable & fabricIdx, const Nullable & nodeId, + const Optional & pin, OperationErrorEnum & err, OperationSourceEnum opSource); + bool Unbolt(chip::EndpointId endpointId, const Nullable & fabricIdx, const Nullable & nodeId, + const Optional & pin, OperationErrorEnum & err, OperationSourceEnum opSource); bool GetUser(chip::EndpointId endpointId, uint16_t userIndex, EmberAfPluginDoorLockUserInfo & user); bool SetUser(chip::EndpointId endpointId, uint16_t userIndex, chip::FabricIndex creator, chip::FabricIndex modifier, diff --git a/examples/lock-app/lock-common/src/LockEndpoint.cpp b/examples/lock-app/lock-common/src/LockEndpoint.cpp index ec090b03f44e3c..2beb010d1523c9 100644 --- a/examples/lock-app/lock-common/src/LockEndpoint.cpp +++ b/examples/lock-app/lock-common/src/LockEndpoint.cpp @@ -18,29 +18,48 @@ #include "LockEndpoint.h" #include #include +#include +#include using chip::to_underlying; using chip::app::DataModel::MakeNullable; -bool LockEndpoint::Lock(const Optional & pin, OperationErrorEnum & err, OperationSourceEnum opSource) +struct LockActionData { - return setLockState(DlLockState::kLocked, pin, err, opSource); + chip::EndpointId endpointId; + DlLockState lockState; + OperationSourceEnum opSource; + Nullable userIndex; + uint16_t credentialIndex; + Nullable fabricIdx; + Nullable nodeId; + bool moving = false; +}; + +static LockActionData gCurrentAction; + +bool LockEndpoint::Lock(const Nullable & fabricIdx, const Nullable & nodeId, + const Optional & pin, OperationErrorEnum & err, OperationSourceEnum opSource) +{ + return setLockState(fabricIdx, nodeId, DlLockState::kLocked, pin, err, opSource); } -bool LockEndpoint::Unlock(const Optional & pin, OperationErrorEnum & err, OperationSourceEnum opSource) +bool LockEndpoint::Unlock(const Nullable & fabricIdx, const Nullable & nodeId, + const Optional & pin, OperationErrorEnum & err, OperationSourceEnum opSource) { if (DoorLockServer::Instance().SupportsUnbolt(mEndpointId)) { // If Unbolt is supported Unlock is supposed to pull the latch - setLockState(DlLockState::kUnlatched, pin, err, opSource); + return setLockState(fabricIdx, nodeId, DlLockState::kUnlatched, pin, err, opSource); } - return setLockState(DlLockState::kUnlocked, pin, err, opSource); + return setLockState(fabricIdx, nodeId, DlLockState::kUnlocked, pin, err, opSource); } -bool LockEndpoint::Unbolt(const Optional & pin, OperationErrorEnum & err, OperationSourceEnum opSource) +bool LockEndpoint::Unbolt(const Nullable & fabricIdx, const Nullable & nodeId, + const Optional & pin, OperationErrorEnum & err, OperationSourceEnum opSource) { - return setLockState(DlLockState::kUnlocked, pin, err, opSource); + return setLockState(fabricIdx, nodeId, DlLockState::kUnlocked, pin, err, opSource); } bool LockEndpoint::GetUser(uint16_t userIndex, EmberAfPluginDoorLockUserInfo & user) const @@ -388,7 +407,8 @@ DlStatus LockEndpoint::SetSchedule(uint8_t holidayIndex, DlScheduleStatus status return DlStatus::kSuccess; } -bool LockEndpoint::setLockState(DlLockState lockState, const Optional & pin, OperationErrorEnum & err, +bool LockEndpoint::setLockState(const Nullable & fabricIdx, const Nullable & nodeId, + DlLockState lockState, const Optional & pin, OperationErrorEnum & err, OperationSourceEnum opSource) { // Assume pin is required until told otherwise @@ -406,13 +426,30 @@ bool LockEndpoint::setLockState(DlLockState lockState, const Optional>(userCredential); - DoorLockServer::Instance().SetLockState(mEndpointId, mLockState, opSource, MakeNullable(static_cast(userIndex + 1)), - userCredentials); + if (gCurrentAction.moving == true) + { + ChipLogProgress(Zcl, + "Lock App: not executing lock action as another lock action is already active [endpointId=%d,userIndex=%u]", + mEndpointId, userIndex); + return false; + } + + gCurrentAction.moving = true; + gCurrentAction.endpointId = mEndpointId; + gCurrentAction.lockState = lockState; + gCurrentAction.opSource = opSource; + gCurrentAction.userIndex = MakeNullable(static_cast(userIndex + 1)); + gCurrentAction.credentialIndex = static_cast(credentialIndex); + gCurrentAction.fabricIdx = fabricIdx; + gCurrentAction.nodeId = nodeId; + + // Do this async as a real lock would do too but use 0s delay to speed up CI tests + chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds16(0), OnLockActionCompleteCallback, nullptr); return true; } +void LockEndpoint::OnLockActionCompleteCallback(chip::System::Layer *, void * callbackContext) +{ + if (gCurrentAction.userIndex.IsNull()) + { + DoorLockServer::Instance().SetLockState(gCurrentAction.endpointId, gCurrentAction.lockState, gCurrentAction.opSource, + NullNullable, NullNullable, gCurrentAction.fabricIdx, gCurrentAction.nodeId); + } + else + { + LockOpCredentials userCredential[] = { { CredentialTypeEnum::kPin, gCurrentAction.credentialIndex } }; + auto userCredentials = MakeNullable>(userCredential); + + DoorLockServer::Instance().SetLockState(gCurrentAction.endpointId, gCurrentAction.lockState, gCurrentAction.opSource, + gCurrentAction.userIndex, userCredentials, gCurrentAction.fabricIdx, + gCurrentAction.nodeId); + } + + // move back to Unlocked after Unlatch + if (gCurrentAction.lockState == DlLockState::kUnlatched) + { + gCurrentAction.lockState = DlLockState::kUnlocked; + + // Do this async as a real lock would do too but use 0s delay to speed up CI tests + chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Seconds16(0), OnLockActionCompleteCallback, nullptr); + } + else + { + gCurrentAction.moving = false; + } +} + bool LockEndpoint::weekDayScheduleInAction(uint16_t userIndex) const { const auto & user = mLockUsers[userIndex]; diff --git a/examples/lock-app/lock-common/src/LockManager.cpp b/examples/lock-app/lock-common/src/LockManager.cpp index f904005340a1fc..8fd1ef5441b139 100644 --- a/examples/lock-app/lock-common/src/LockManager.cpp +++ b/examples/lock-app/lock-common/src/LockManager.cpp @@ -134,7 +134,8 @@ bool LockManager::SendLockAlarm(chip::EndpointId endpointId, AlarmCodeEnum alarm return lockEndpoint->SendLockAlarm(alarmCode); } -bool LockManager::Lock(chip::EndpointId endpointId, const Optional & pin, OperationErrorEnum & err, +bool LockManager::Lock(chip::EndpointId endpointId, const Nullable & fabricIdx, + const Nullable & nodeId, const Optional & pin, OperationErrorEnum & err, OperationSourceEnum opSource) { auto lockEndpoint = getEndpoint(endpointId); @@ -143,10 +144,11 @@ bool LockManager::Lock(chip::EndpointId endpointId, const OptionalLock(pin, err, opSource); + return lockEndpoint->Lock(fabricIdx, nodeId, pin, err, opSource); } -bool LockManager::Unlock(chip::EndpointId endpointId, const Optional & pin, OperationErrorEnum & err, +bool LockManager::Unlock(chip::EndpointId endpointId, const Nullable & fabricIdx, + const Nullable & nodeId, const Optional & pin, OperationErrorEnum & err, OperationSourceEnum opSource) { auto lockEndpoint = getEndpoint(endpointId); @@ -155,10 +157,11 @@ bool LockManager::Unlock(chip::EndpointId endpointId, const OptionalUnlock(pin, err, opSource); + return lockEndpoint->Unlock(fabricIdx, nodeId, pin, err, opSource); } -bool LockManager::Unbolt(chip::EndpointId endpointId, const Optional & pin, OperationErrorEnum & err, +bool LockManager::Unbolt(chip::EndpointId endpointId, const Nullable & fabricIdx, + const Nullable & nodeId, const Optional & pin, OperationErrorEnum & err, OperationSourceEnum opSource) { auto lockEndpoint = getEndpoint(endpointId); @@ -167,7 +170,7 @@ bool LockManager::Unbolt(chip::EndpointId endpointId, const OptionalUnbolt(pin, err, opSource); + return lockEndpoint->Unbolt(fabricIdx, nodeId, pin, err, opSource); } bool LockManager::GetUser(chip::EndpointId endpointId, uint16_t userIndex, EmberAfPluginDoorLockUserInfo & user) diff --git a/examples/lock-app/lock-common/src/ZCLDoorLockCallbacks.cpp b/examples/lock-app/lock-common/src/ZCLDoorLockCallbacks.cpp index bbecc8ef6ce178..2b649db7443d42 100644 --- a/examples/lock-app/lock-common/src/ZCLDoorLockCallbacks.cpp +++ b/examples/lock-app/lock-common/src/ZCLDoorLockCallbacks.cpp @@ -28,22 +28,25 @@ using namespace chip::app::Clusters::DoorLock; // should wait for door to be locked on lock command and return success) but // door lock server should check pin before even calling the lock-door // callback. -bool emberAfPluginDoorLockOnDoorLockCommand(chip::EndpointId endpointId, const Optional & pinCode, +bool emberAfPluginDoorLockOnDoorLockCommand(chip::EndpointId endpointId, const Nullable & fabricIdx, + const Nullable & nodeId, const Optional & pinCode, OperationErrorEnum & err) { - return LockManager::Instance().Lock(endpointId, pinCode, err, OperationSourceEnum::kRemote); + return LockManager::Instance().Lock(endpointId, fabricIdx, nodeId, pinCode, err, OperationSourceEnum::kRemote); } -bool emberAfPluginDoorLockOnDoorUnlockCommand(chip::EndpointId endpointId, const Optional & pinCode, +bool emberAfPluginDoorLockOnDoorUnlockCommand(chip::EndpointId endpointId, const Nullable & fabricIdx, + const Nullable & nodeId, const Optional & pinCode, OperationErrorEnum & err) { - return LockManager::Instance().Unlock(endpointId, pinCode, err, OperationSourceEnum::kRemote); + return LockManager::Instance().Unlock(endpointId, fabricIdx, nodeId, pinCode, err, OperationSourceEnum::kRemote); } -bool emberAfPluginDoorLockOnDoorUnboltCommand(chip::EndpointId endpointId, const Optional & pinCode, +bool emberAfPluginDoorLockOnDoorUnboltCommand(chip::EndpointId endpointId, const Nullable & fabricIdx, + const Nullable & nodeId, const Optional & pinCode, OperationErrorEnum & err) { - return LockManager::Instance().Unbolt(endpointId, pinCode, err, OperationSourceEnum::kRemote); + return LockManager::Instance().Unbolt(endpointId, fabricIdx, nodeId, pinCode, err, OperationSourceEnum::kRemote); } bool emberAfPluginDoorLockGetUser(chip::EndpointId endpointId, uint16_t userIndex, EmberAfPluginDoorLockUserInfo & user) diff --git a/src/app/clusters/door-lock-server/door-lock-server-callback.cpp b/src/app/clusters/door-lock-server/door-lock-server-callback.cpp index 9db680168dd637..a1644286c1af1a 100644 --- a/src/app/clusters/door-lock-server/door-lock-server-callback.cpp +++ b/src/app/clusters/door-lock-server/door-lock-server-callback.cpp @@ -45,21 +45,27 @@ using namespace chip::app::Clusters::DoorLock; // ============================================================================= bool __attribute__((weak)) -emberAfPluginDoorLockOnDoorLockCommand(chip::EndpointId endpointId, const Optional & pinCode, OperationErrorEnum & err) +emberAfPluginDoorLockOnDoorLockCommand(chip::EndpointId endpointId, const Nullable & fabricIdx, + const Nullable & nodeId, const Optional & pinCode, + OperationErrorEnum & err) { err = OperationErrorEnum::kUnspecified; return false; } bool __attribute__((weak)) -emberAfPluginDoorLockOnDoorUnlockCommand(chip::EndpointId endpointId, const Optional & pinCode, OperationErrorEnum & err) +emberAfPluginDoorLockOnDoorUnlockCommand(chip::EndpointId endpointId, const Nullable & fabricIdx, + const Nullable & nodeId, const Optional & pinCode, + OperationErrorEnum & err) { err = OperationErrorEnum::kUnspecified; return false; } bool __attribute__((weak)) -emberAfPluginDoorLockOnDoorUnboltCommand(chip::EndpointId endpointId, const Optional & pinCode, OperationErrorEnum & err) +emberAfPluginDoorLockOnDoorUnboltCommand(chip::EndpointId endpointId, const Nullable & fabricIdx, + const Nullable & nodeId, const Optional & pinCode, + OperationErrorEnum & err) { err = OperationErrorEnum::kUnspecified; return false; 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 dbfd723ad8cfff..407afc0af5f001 100644 --- a/src/app/clusters/door-lock-server/door-lock-server.cpp +++ b/src/app/clusters/door-lock-server/door-lock-server.cpp @@ -104,13 +104,11 @@ bool DoorLockServer::SetLockState(chip::EndpointId endpointId, DlLockState newLo } bool DoorLockServer::SetLockState(chip::EndpointId endpointId, DlLockState newLockState, OperationSourceEnum opSource, - const Nullable & userIndex, const Nullable> & credentials) + const Nullable & userIndex, const Nullable> & credentials, + const Nullable & fabricIdx, const Nullable & nodeId) { bool success = SetLockState(endpointId, newLockState); - // Remote operations are handled separately as they use more data unavailable here - VerifyOrReturnError(OperationSourceEnum::kRemote != opSource, success); - // DlLockState::kNotFullyLocked has no appropriate event to send. Also it is unclear whether // it should schedule auto-relocking. So skip it here. Check for supported states explicitly // to handle possible enum extending in future. @@ -130,8 +128,13 @@ bool DoorLockServer::SetLockState(chip::EndpointId endpointId, DlLockState newLo opType = LockOperationTypeEnum::kUnlatch; } - SendLockOperationEvent(endpointId, opType, opSource, OperationErrorEnum::kUnspecified, userIndex, Nullable(), - Nullable(), credentials, success); + if (OperationSourceEnum::kRemote == opSource && (fabricIdx.IsNull() || nodeId.IsNull())) + { + ChipLogError(Zcl, "Received SetLockState for remote operation without fabricIdx or nodeId"); + } + + SendLockOperationEvent(endpointId, opType, opSource, OperationErrorEnum::kUnspecified, userIndex, fabricIdx, nodeId, + credentials, success); // Reset wrong entry attempts (in case there were any incorrect credentials presented before) if lock/unlock was a success // and a valid credential was presented. @@ -3428,7 +3431,7 @@ bool DoorLockServer::HandleRemoteLockOperation(chip::app::CommandHandler * comma } // credentials check succeeded, try to lock/unlock door - success = opHandler(endpoint, pinCode, reason); + success = opHandler(endpoint, MakeNullable(getFabricIndex(commandObj)), MakeNullable(getNodeId(commandObj)), pinCode, reason); // The app should trigger the lock state change as it may take a while before the lock actually locks/unlocks exit: if (!success && reason == OperationErrorEnum::kInvalidCredential) @@ -3464,23 +3467,15 @@ bool DoorLockServer::HandleRemoteLockOperation(chip::app::CommandHandler * comma credentials.SetNonNull(foundCred); } - // Failed Unlatch requests SHALL generate only a LockOperationError event with LockOperationType set to Unlock - if (LockOperationTypeEnum::kUnlatch == opType && !success) + if (!success) { - opType = LockOperationTypeEnum::kUnlock; - } - - SendLockOperationEvent(endpoint, opType, OperationSourceEnum::kRemote, reason, pinUserIdx, - Nullable(getFabricIndex(commandObj)), Nullable(getNodeId(commandObj)), - credentials, success); + // Failed Unlatch requests SHALL generate only a LockOperationError event with LockOperationType set to Unlock + if (LockOperationTypeEnum::kUnlatch == opType) + { + opType = LockOperationTypeEnum::kUnlock; + } - // SHALL generate a LockOperation event with LockOperationType set to Unlatch when the unlatched state is reached and a - // LockOperation event with LockOperationType set to Unlock when the lock successfully completes the unlock. But as the current - // implementation here is sending LockOperation events immediately we're sending both events immediately. - // https://github.com/project-chip/connectedhomeip/issues/26925 - if (LockOperationTypeEnum::kUnlatch == opType && success) - { - SendLockOperationEvent(endpoint, LockOperationTypeEnum::kUnlock, OperationSourceEnum::kRemote, reason, pinUserIdx, + SendLockOperationEvent(endpoint, opType, OperationSourceEnum::kRemote, reason, pinUserIdx, Nullable(getFabricIndex(commandObj)), Nullable(getNodeId(commandObj)), credentials, success); } 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 bb2ce23571ba8b..72492843bb1b44 100644 --- a/src/app/clusters/door-lock-server/door-lock-server.h +++ b/src/app/clusters/door-lock-server/door-lock-server.h @@ -59,7 +59,17 @@ using chip::app::DataModel::NullNullable; using CredentialStruct = chip::app::Clusters::DoorLock::Structs::CredentialStruct::Type; using LockOpCredentials = CredentialStruct; -typedef bool (*RemoteLockOpHandler)(chip::EndpointId endpointId, const Optional & pinCode, +/** + * Handler for executing remote lock operations. + * + * @param endpointId endpoint for which remote lock command is called + * @param fabricIdx fabric index responsible for operating the lock + * @param nodeId node id responsible for operating the lock + * @param pinCode PIN code (optional) + * @param err error code if door locking failed (set only if retval==false) + */ +typedef bool (*RemoteLockOpHandler)(chip::EndpointId endpointId, const Nullable & fabricIdx, + const Nullable & nodeId, const Optional & pinCode, OperationErrorEnum & err); static constexpr size_t DOOR_LOCK_MAX_USER_NAME_SIZE = 10; /**< Maximum size of the user name (in characters). */ @@ -95,12 +105,16 @@ class DoorLockServer * @param endpointId ID of the endpoint to the lock state * @param newLockState new lock state * @param opSource source of the operation (will be used in the event). + * @param fabricIdx fabric index responsible for operating the lock + * @param nodeId node id responsible for operating the lock * * @return true on success, false on failure. */ bool SetLockState(chip::EndpointId endpointId, DlLockState newLockState, OperationSourceEnum opSource, const Nullable & userIndex = NullNullable, - const Nullable> & credentials = NullNullable); + const Nullable> & credentials = NullNullable, + const Nullable & fabricIdx = NullNullable, + const Nullable & nodeId = NullNullable); /** * Updates the LockState attribute with new value. @@ -421,8 +435,8 @@ class DoorLockServer * @param opSource operation source (remote, keypad, auto, etc) * @param opErr operation error code (if opSuccess == false) * @param userId user id - * @param fabricIdx fabric index - * @param nodeId node id + * @param fabricIdx fabric index responsible for operating the lock + * @param nodeId node id responsible for operating the lock * @param credList list of credentials used in lock operation (can be NULL if no credentials were used) * @param credListSize size of credentials list (if 0, then no credentials were used) * @param opSuccess flags if operation was successful or not @@ -899,39 +913,48 @@ emberAfPluginDoorLockOnUnhandledAttributeChange(chip::EndpointId EndpointId, con * @brief User handler for LockDoor command (server) * * @param endpointId endpoint for which LockDoor command is called + * @param fabricIdx fabric index responsible for operating the lock + * @param nodeId node id responsible for operating the lock * @param pinCode PIN code (optional) * @param err error code if door locking failed (set only if retval==false) * * @retval true on success * @retval false if error happenned (err should be set to appropriate error code) */ -bool emberAfPluginDoorLockOnDoorLockCommand(chip::EndpointId endpointId, const Optional & pinCode, +bool emberAfPluginDoorLockOnDoorLockCommand(chip::EndpointId endpointId, const Nullable & fabricIdx, + const Nullable & nodeId, const Optional & pinCode, OperationErrorEnum & err); /** * @brief User handler for UnlockDoor command (server) * * @param endpointId endpoint for which UnlockDoor command is called + * @param fabricIdx fabric index responsible for operating the lock + * @param nodeId node id responsible for operating the lock * @param pinCode PIN code (optional) * @param err error code if door unlocking failed (set only if retval==false) * * @retval true on success * @retval false if error happenned (err should be set to appropriate error code) */ -bool emberAfPluginDoorLockOnDoorUnlockCommand(chip::EndpointId endpointId, const Optional & pinCode, +bool emberAfPluginDoorLockOnDoorUnlockCommand(chip::EndpointId endpointId, const Nullable & fabricIdx, + const Nullable & nodeId, const Optional & pinCode, OperationErrorEnum & err); /** * @brief User handler for UnboltDoor command (server) * * @param endpointId endpoint for which UnboltDoor command is called + * @param fabricIdx fabric index responsible for operating the lock + * @param nodeId node id responsible for operating the lock * @param pinCode PIN code (optional) * @param err error code if door unbolting failed (set only if retval==false) * * @retval true on success * @retval false if error happenned (err should be set to appropriate error code) */ -bool emberAfPluginDoorLockOnDoorUnboltCommand(chip::EndpointId endpointId, const Optional & pinCode, +bool emberAfPluginDoorLockOnDoorUnboltCommand(chip::EndpointId endpointId, const Nullable & fabricIdx, + const Nullable & nodeId, const Optional & pinCode, OperationErrorEnum & err); /**