Skip to content

Commit

Permalink
Generate lock operation events after lock state has changed (#27991)
Browse files Browse the repository at this point in the history
* #26925 generate lock operation events after lock state has changed

* #26925 pass fabricIdx and nodeId instead of commandObj

* #26925 async execution of lock actions in example app

* #26925 change async delays to 0s to not affect certification tests

* #26925 add documentation

* #26925 add error log if fabricIdx or nodeId is missing on remote actions

* #26925 update check for missing fabricIdx and nodeId
  • Loading branch information
mmarc authored and pull[bot] committed Aug 29, 2023
1 parent 90cf9ab commit 36ed896
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 68 deletions.
14 changes: 10 additions & 4 deletions examples/lock-app/lock-common/include/LockEndpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,12 @@ class LockEndpoint

inline chip::EndpointId GetEndpointId() const { return mEndpointId; }

bool Lock(const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err, OperationSourceEnum opSource);
bool Unlock(const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err, OperationSourceEnum opSource);
bool Unbolt(const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err, OperationSourceEnum opSource);
bool Lock(const Nullable<chip::FabricIndex> & fabricIdx, const Nullable<chip::NodeId> & nodeId,
const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err, OperationSourceEnum opSource);
bool Unlock(const Nullable<chip::FabricIndex> & fabricIdx, const Nullable<chip::NodeId> & nodeId,
const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err, OperationSourceEnum opSource);
bool Unbolt(const Nullable<chip::FabricIndex> & fabricIdx, const Nullable<chip::NodeId> & nodeId,
const Optional<chip::ByteSpan> & 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,
Expand Down Expand Up @@ -98,13 +101,16 @@ class LockEndpoint
OperatingModeEnum operatingMode);

private:
bool setLockState(DlLockState lockState, const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err,
bool setLockState(const Nullable<chip::FabricIndex> & fabricIdx, const Nullable<chip::NodeId> & nodeId, DlLockState lockState,
const Optional<chip::ByteSpan> & 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;
Expand Down
12 changes: 6 additions & 6 deletions examples/lock-app/lock-common/include/LockManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ class LockManager

bool SendLockAlarm(chip::EndpointId endpointId, AlarmCodeEnum alarmCode);

bool Lock(chip::EndpointId endpointId, const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err,
OperationSourceEnum opSource);
bool Unlock(chip::EndpointId endpointId, const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err,
OperationSourceEnum opSource);
bool Unbolt(chip::EndpointId endpointId, const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err,
OperationSourceEnum opSource);
bool Lock(chip::EndpointId endpointId, const Nullable<chip::FabricIndex> & fabricIdx, const Nullable<chip::NodeId> & nodeId,
const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err, OperationSourceEnum opSource);
bool Unlock(chip::EndpointId endpointId, const Nullable<chip::FabricIndex> & fabricIdx, const Nullable<chip::NodeId> & nodeId,
const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err, OperationSourceEnum opSource);
bool Unbolt(chip::EndpointId endpointId, const Nullable<chip::FabricIndex> & fabricIdx, const Nullable<chip::NodeId> & nodeId,
const Optional<chip::ByteSpan> & 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,
Expand Down
110 changes: 96 additions & 14 deletions examples/lock-app/lock-common/src/LockEndpoint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,48 @@
#include "LockEndpoint.h"
#include <app-common/zap-generated/attributes/Accessors.h>
#include <cstring>
#include <platform/CHIPDeviceLayer.h>
#include <platform/internal/CHIPDeviceLayerInternal.h>

using chip::to_underlying;
using chip::app::DataModel::MakeNullable;

bool LockEndpoint::Lock(const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err, OperationSourceEnum opSource)
struct LockActionData
{
return setLockState(DlLockState::kLocked, pin, err, opSource);
chip::EndpointId endpointId;
DlLockState lockState;
OperationSourceEnum opSource;
Nullable<uint16_t> userIndex;
uint16_t credentialIndex;
Nullable<chip::FabricIndex> fabricIdx;
Nullable<chip::NodeId> nodeId;
bool moving = false;
};

static LockActionData gCurrentAction;

bool LockEndpoint::Lock(const Nullable<chip::FabricIndex> & fabricIdx, const Nullable<chip::NodeId> & nodeId,
const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err, OperationSourceEnum opSource)
{
return setLockState(fabricIdx, nodeId, DlLockState::kLocked, pin, err, opSource);
}

bool LockEndpoint::Unlock(const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err, OperationSourceEnum opSource)
bool LockEndpoint::Unlock(const Nullable<chip::FabricIndex> & fabricIdx, const Nullable<chip::NodeId> & nodeId,
const Optional<chip::ByteSpan> & 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<chip::ByteSpan> & pin, OperationErrorEnum & err, OperationSourceEnum opSource)
bool LockEndpoint::Unbolt(const Nullable<chip::FabricIndex> & fabricIdx, const Nullable<chip::NodeId> & nodeId,
const Optional<chip::ByteSpan> & 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
Expand Down Expand Up @@ -388,7 +407,8 @@ DlStatus LockEndpoint::SetSchedule(uint8_t holidayIndex, DlScheduleStatus status
return DlStatus::kSuccess;
}

bool LockEndpoint::setLockState(DlLockState lockState, const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err,
bool LockEndpoint::setLockState(const Nullable<chip::FabricIndex> & fabricIdx, const Nullable<chip::NodeId> & nodeId,
DlLockState lockState, const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err,
OperationSourceEnum opSource)
{
// Assume pin is required until told otherwise
Expand All @@ -406,13 +426,30 @@ bool LockEndpoint::setLockState(DlLockState lockState, const Optional<chip::Byte
ChipLogProgress(Zcl, "Door Lock App: setting door lock state to \"%s\" [endpointId=%d]", lockStateToString(lockState),
mEndpointId);

DoorLockServer::Instance().SetLockState(mEndpointId, lockState);
if (gCurrentAction.moving == true)
{
ChipLogProgress(Zcl, "Lock App: not executing lock action as another lock action is already active [endpointId=%d]",
mEndpointId);
return false;
}

gCurrentAction.moving = true;
gCurrentAction.endpointId = mEndpointId;
gCurrentAction.lockState = lockState;
gCurrentAction.opSource = opSource;
gCurrentAction.userIndex = NullNullable;
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;
}

ChipLogError(Zcl, "Door Lock App: PIN code is not specified, but it is required [endpointId=%d]", mEndpointId);

err = OperationErrorEnum::kInvalidCredential;
return false;
}

Expand Down Expand Up @@ -470,15 +507,60 @@ bool LockEndpoint::setLockState(DlLockState lockState, const Optional<chip::Byte
"Lock App: specified PIN code was found in the database, setting door lock state to \"%s\" [endpointId=%d,userIndex=%u]",
lockStateToString(lockState), mEndpointId, userIndex);

mLockState = lockState;
LockOpCredentials userCredential[] = { { CredentialTypeEnum::kPin, uint16_t(credentialIndex) } };
auto userCredentials = MakeNullable<List<const LockOpCredentials>>(userCredential);
DoorLockServer::Instance().SetLockState(mEndpointId, mLockState, opSource, MakeNullable(static_cast<uint16_t>(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<uint16_t>(userIndex + 1));
gCurrentAction.credentialIndex = static_cast<uint16_t>(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<List<const LockOpCredentials>>(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];
Expand Down
15 changes: 9 additions & 6 deletions examples/lock-app/lock-common/src/LockManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,8 @@ bool LockManager::SendLockAlarm(chip::EndpointId endpointId, AlarmCodeEnum alarm
return lockEndpoint->SendLockAlarm(alarmCode);
}

bool LockManager::Lock(chip::EndpointId endpointId, const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err,
bool LockManager::Lock(chip::EndpointId endpointId, const Nullable<chip::FabricIndex> & fabricIdx,
const Nullable<chip::NodeId> & nodeId, const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err,
OperationSourceEnum opSource)
{
auto lockEndpoint = getEndpoint(endpointId);
Expand All @@ -143,10 +144,11 @@ bool LockManager::Lock(chip::EndpointId endpointId, const Optional<chip::ByteSpa
ChipLogError(Zcl, "Unable to lock the door - endpoint does not exist or not initialized [endpointId=%d]", endpointId);
return false;
}
return lockEndpoint->Lock(pin, err, opSource);
return lockEndpoint->Lock(fabricIdx, nodeId, pin, err, opSource);
}

bool LockManager::Unlock(chip::EndpointId endpointId, const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err,
bool LockManager::Unlock(chip::EndpointId endpointId, const Nullable<chip::FabricIndex> & fabricIdx,
const Nullable<chip::NodeId> & nodeId, const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err,
OperationSourceEnum opSource)
{
auto lockEndpoint = getEndpoint(endpointId);
Expand All @@ -155,10 +157,11 @@ bool LockManager::Unlock(chip::EndpointId endpointId, const Optional<chip::ByteS
ChipLogError(Zcl, "Unable to unlock the door - endpoint does not exist or not initialized [endpointId=%d]", endpointId);
return false;
}
return lockEndpoint->Unlock(pin, err, opSource);
return lockEndpoint->Unlock(fabricIdx, nodeId, pin, err, opSource);
}

bool LockManager::Unbolt(chip::EndpointId endpointId, const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err,
bool LockManager::Unbolt(chip::EndpointId endpointId, const Nullable<chip::FabricIndex> & fabricIdx,
const Nullable<chip::NodeId> & nodeId, const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err,
OperationSourceEnum opSource)
{
auto lockEndpoint = getEndpoint(endpointId);
Expand All @@ -167,7 +170,7 @@ bool LockManager::Unbolt(chip::EndpointId endpointId, const Optional<chip::ByteS
ChipLogError(Zcl, "Unable to unbolt the door - endpoint does not exist or not initialized [endpointId=%d]", endpointId);
return false;
}
return lockEndpoint->Unbolt(pin, err, opSource);
return lockEndpoint->Unbolt(fabricIdx, nodeId, pin, err, opSource);
}

bool LockManager::GetUser(chip::EndpointId endpointId, uint16_t userIndex, EmberAfPluginDoorLockUserInfo & user)
Expand Down
15 changes: 9 additions & 6 deletions examples/lock-app/lock-common/src/ZCLDoorLockCallbacks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ByteSpan> & pinCode,
bool emberAfPluginDoorLockOnDoorLockCommand(chip::EndpointId endpointId, const Nullable<chip::FabricIndex> & fabricIdx,
const Nullable<chip::NodeId> & nodeId, const Optional<ByteSpan> & 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<ByteSpan> & pinCode,
bool emberAfPluginDoorLockOnDoorUnlockCommand(chip::EndpointId endpointId, const Nullable<chip::FabricIndex> & fabricIdx,
const Nullable<chip::NodeId> & nodeId, const Optional<ByteSpan> & 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<ByteSpan> & pinCode,
bool emberAfPluginDoorLockOnDoorUnboltCommand(chip::EndpointId endpointId, const Nullable<chip::FabricIndex> & fabricIdx,
const Nullable<chip::NodeId> & nodeId, const Optional<ByteSpan> & 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)
Expand Down
12 changes: 9 additions & 3 deletions src/app/clusters/door-lock-server/door-lock-server-callback.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,27 @@ using namespace chip::app::Clusters::DoorLock;
// =============================================================================

bool __attribute__((weak))
emberAfPluginDoorLockOnDoorLockCommand(chip::EndpointId endpointId, const Optional<ByteSpan> & pinCode, OperationErrorEnum & err)
emberAfPluginDoorLockOnDoorLockCommand(chip::EndpointId endpointId, const Nullable<chip::FabricIndex> & fabricIdx,
const Nullable<chip::NodeId> & nodeId, const Optional<ByteSpan> & pinCode,
OperationErrorEnum & err)
{
err = OperationErrorEnum::kUnspecified;
return false;
}

bool __attribute__((weak))
emberAfPluginDoorLockOnDoorUnlockCommand(chip::EndpointId endpointId, const Optional<ByteSpan> & pinCode, OperationErrorEnum & err)
emberAfPluginDoorLockOnDoorUnlockCommand(chip::EndpointId endpointId, const Nullable<chip::FabricIndex> & fabricIdx,
const Nullable<chip::NodeId> & nodeId, const Optional<ByteSpan> & pinCode,
OperationErrorEnum & err)
{
err = OperationErrorEnum::kUnspecified;
return false;
}

bool __attribute__((weak))
emberAfPluginDoorLockOnDoorUnboltCommand(chip::EndpointId endpointId, const Optional<ByteSpan> & pinCode, OperationErrorEnum & err)
emberAfPluginDoorLockOnDoorUnboltCommand(chip::EndpointId endpointId, const Nullable<chip::FabricIndex> & fabricIdx,
const Nullable<chip::NodeId> & nodeId, const Optional<ByteSpan> & pinCode,
OperationErrorEnum & err)
{
err = OperationErrorEnum::kUnspecified;
return false;
Expand Down
Loading

0 comments on commit 36ed896

Please sign in to comment.