Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add unbolt support to door lock cluster #26595

Merged
merged 20 commits into from
May 30, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
34a1737
add unbolt support to door lock cluster
mmarc May 15, 2023
88e56c5
Adjust Darwin availability annotation to fix codegen failure.
bzbarsky-apple May 16, 2023
5dd8128
Merge remote-tracking branch 'origin/master' into feature/door_lock_u…
mmarc May 20, 2023
0caabce
add unbolt callback to door lock server
mmarc May 23, 2023
f5cb1d4
handle setting the LockState to Unlatched
mmarc May 24, 2023
012b2be
Merge remote-tracking branch 'origin/master' into feature/door_lock_u…
mmarc May 24, 2023
9b9b9e4
apply restyled.io changes
mmarc May 24, 2023
d11b324
set lock state to Unlocked after Unlatch operation
mmarc May 24, 2023
ef41abd
update door lock tests to handle Unlatched state
mmarc May 24, 2023
ef8f4bd
Merge remote-tracking branch 'origin/master' into feature/door_lock_u…
mmarc May 25, 2023
729bf93
update door lock tests
mmarc May 25, 2023
8ec73af
Merge remote-tracking branch 'origin/master' into feature/door_lock_u…
mmarc May 25, 2023
b27dd44
move handling of latch pulling to door lock example
mmarc May 25, 2023
7c00dd3
send lock operation event Unlatch for unlatched state
mmarc May 26, 2023
0aa35a9
Merge remote-tracking branch 'origin/master' into feature/door_lock_u…
mmarc May 26, 2023
2b6befc
send LockOperationEvent Unlatch immediately within HandleRemoteLockOp…
mmarc May 26, 2023
1c764dd
fix duplicate test step
mmarc May 27, 2023
483b61a
apply restyled changes
mmarc May 27, 2023
f546500
Merge remote-tracking branch 'origin/master' into feature/door_lock_u…
mmarc May 29, 2023
cb5458b
add reference to door lock server issue
mmarc May 29, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2820,6 +2820,10 @@ server cluster DoorLock = 257 {
nullable CredentialStruct credential = 0;
}

request struct UnboltDoorRequest {
optional OCTET_STRING PINCode = 0;
}

response struct GetUserResponse = 28 {
INT16U userIndex = 0;
nullable CHAR_STRING userName = 1;
Expand Down Expand Up @@ -2861,6 +2865,7 @@ server cluster DoorLock = 257 {
timed command access(invoke: administer) SetCredential(SetCredentialRequest): SetCredentialResponse = 34;
command access(invoke: administer) GetCredentialStatus(GetCredentialStatusRequest): GetCredentialStatusResponse = 36;
timed command access(invoke: administer) ClearCredential(ClearCredentialRequest): DefaultSuccess = 38;
timed command UnboltDoor(UnboltDoorRequest): DefaultSuccess = 39;
}

/** Provides an interface for controlling and adjusting automatic window coverings. */
Expand Down Expand Up @@ -5175,7 +5180,7 @@ endpoint 1 {
ram attribute wrongCodeEntryLimit default = 3;
ram attribute userCodeTemporaryDisableTime default = 10;
ram attribute requirePINforRemoteOperation default = 0;
ram attribute featureMap default = 0xD13;
ram attribute featureMap default = 0x1D13;
ram attribute clusterRevision default = 6;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13147,6 +13147,14 @@
"source": "client",
"incoming": 1,
"outgoing": 0
},
{
"name": "UnboltDoor",
"code": 39,
"mfgCode": null,
"source": "client",
"incoming": 1,
"outgoing": 0
}
],
"attributes": [
Expand Down Expand Up @@ -13836,7 +13844,7 @@
"storageOption": "RAM",
"singleton": 0,
"bounded": 0,
"defaultValue": "0xD13",
"defaultValue": "0x1D13",
"reportable": 1,
"minInterval": 1,
"maxInterval": 65534,
Expand Down
2 changes: 2 additions & 0 deletions examples/lock-app/cc13x2x7_26x2x7/src/LockManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,8 @@ const char * LockManager::lockStateToString(DlLockState lockState) const
return "Locked";
case DlLockState::kUnlocked:
return "Unlocked";
case DlLockState::kUnlatched:
return "Unlatched";
case DlLockState::kUnknownEnumValue:
break;
default:
Expand Down
1 change: 1 addition & 0 deletions examples/lock-app/lock-common/include/LockEndpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class LockEndpoint

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 GetUser(uint16_t userIndex, EmberAfPluginDoorLockUserInfo & user) const;
bool SetUser(uint16_t userIndex, chip::FabricIndex creator, chip::FabricIndex modifier, const chip::CharSpan & userName,
Expand Down
2 changes: 2 additions & 0 deletions examples/lock-app/lock-common/include/LockManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class LockManager
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 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
7 changes: 6 additions & 1 deletion examples/lock-app/lock-common/lock-app.matter
Original file line number Diff line number Diff line change
Expand Up @@ -2152,6 +2152,10 @@ server cluster DoorLock = 257 {
nullable CredentialStruct credential = 0;
}

request struct UnboltDoorRequest {
optional OCTET_STRING PINCode = 0;
}

response struct GetWeekDayScheduleResponse = 12 {
INT8U weekDayIndex = 0;
INT16U userIndex = 1;
Expand Down Expand Up @@ -2224,6 +2228,7 @@ server cluster DoorLock = 257 {
timed command access(invoke: administer) SetCredential(SetCredentialRequest): SetCredentialResponse = 34;
command access(invoke: administer) GetCredentialStatus(GetCredentialStatusRequest): GetCredentialStatusResponse = 36;
timed command access(invoke: administer) ClearCredential(ClearCredentialRequest): DefaultSuccess = 38;
timed command UnboltDoor(UnboltDoorRequest): DefaultSuccess = 39;
}

endpoint 0 {
Expand Down Expand Up @@ -2593,7 +2598,7 @@ endpoint 1 {
ram attribute wrongCodeEntryLimit default = 3;
ram attribute userCodeTemporaryDisableTime default = 10;
ram attribute requirePINforRemoteOperation default = 0;
ram attribute featureMap default = 0xDB3;
ram attribute featureMap default = 0x1DB3;
ram attribute clusterRevision default = 6;
}
}
Expand Down
10 changes: 9 additions & 1 deletion examples/lock-app/lock-common/lock-app.zap
Original file line number Diff line number Diff line change
Expand Up @@ -7023,6 +7023,14 @@
"source": "client",
"incoming": 1,
"outgoing": 0
},
{
"name": "UnboltDoor",
"code": 39,
"mfgCode": null,
"source": "client",
"incoming": 1,
"outgoing": 0
}
],
"attributes": [
Expand Down Expand Up @@ -7736,7 +7744,7 @@
"storageOption": "RAM",
"singleton": 0,
"bounded": 0,
"defaultValue": "0xDB3",
"defaultValue": "0x1DB3",
"reportable": 1,
"minInterval": 1,
"maxInterval": 65534,
Expand Down
11 changes: 11 additions & 0 deletions examples/lock-app/lock-common/src/LockEndpoint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,17 @@ bool LockEndpoint::Lock(const Optional<chip::ByteSpan> & pin, OperationErrorEnum
}

bool LockEndpoint::Unlock(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(DlLockState::kUnlocked, pin, err, opSource);
}

bool LockEndpoint::Unbolt(const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err, OperationSourceEnum opSource)
{
return setLockState(DlLockState::kUnlocked, pin, err, opSource);
}
Expand Down
12 changes: 12 additions & 0 deletions examples/lock-app/lock-common/src/LockManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,18 @@ bool LockManager::Unlock(chip::EndpointId endpointId, const Optional<chip::ByteS
return lockEndpoint->Unlock(pin, err, opSource);
}

bool LockManager::Unbolt(chip::EndpointId endpointId, const Optional<chip::ByteSpan> & pin, OperationErrorEnum & err,
OperationSourceEnum opSource)
{
auto lockEndpoint = getEndpoint(endpointId);
if (nullptr == lockEndpoint)
{
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);
}

bool LockManager::GetUser(chip::EndpointId endpointId, uint16_t userIndex, EmberAfPluginDoorLockUserInfo & user)
{
auto lockEndpoint = getEndpoint(endpointId);
Expand Down
6 changes: 6 additions & 0 deletions examples/lock-app/lock-common/src/ZCLDoorLockCallbacks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@ bool emberAfPluginDoorLockOnDoorUnlockCommand(chip::EndpointId endpointId, const
return LockManager::Instance().Unlock(endpointId, pinCode, err, OperationSourceEnum::kRemote);
}

bool emberAfPluginDoorLockOnDoorUnboltCommand(chip::EndpointId endpointId, const Optional<ByteSpan> & pinCode,
OperationErrorEnum & err)
{
return LockManager::Instance().Unbolt(endpointId, pinCode, err, OperationSourceEnum::kRemote);
}

bool emberAfPluginDoorLockGetUser(chip::EndpointId endpointId, uint16_t userIndex, EmberAfPluginDoorLockUserInfo & user)
{
return LockManager::Instance().GetUser(endpointId, userIndex, user);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,13 @@ emberAfPluginDoorLockOnDoorUnlockCommand(chip::EndpointId endpointId, const Opti
return false;
}

bool __attribute__((weak))
emberAfPluginDoorLockOnDoorUnboltCommand(chip::EndpointId endpointId, const Optional<ByteSpan> & pinCode, OperationErrorEnum & err)
{
err = OperationErrorEnum::kUnspecified;
return false;
}

void __attribute__((weak)) emberAfPluginDoorLockOnAutoRelock(chip::EndpointId endpointId) {}

// =============================================================================
Expand Down
26 changes: 25 additions & 1 deletion src/app/clusters/door-lock-server/door-lock-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,9 @@ bool DoorLockServer::SetLockState(chip::EndpointId endpointId, DlLockState newLo
// 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.
VerifyOrReturnError(DlLockState::kLocked == newLockState || DlLockState::kUnlocked == newLockState, success);
VerifyOrReturnError(DlLockState::kLocked == newLockState || DlLockState::kUnlocked == newLockState ||
DlLockState::kUnlatched == newLockState,
success);

// Send LockOperation event
auto opType = (DlLockState::kLocked == newLockState) ? LockOperationTypeEnum::kLock : LockOperationTypeEnum::kUnlock;
Expand Down Expand Up @@ -3563,6 +3565,28 @@ bool emberAfDoorLockClusterUnlockWithTimeoutCallback(
return true;
}

bool emberAfDoorLockClusterUnboltDoorCallback(
chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath,
const chip::app::Clusters::DoorLock::Commands::UnboltDoor::DecodableType & commandData)
{
emberAfDoorLockClusterPrintln("Received command: UnboltDoor");

if (DoorLockServer::Instance().HandleRemoteLockOperation(commandObj, commandPath, LockOperationTypeEnum::kUnlock,
emberAfPluginDoorLockOnDoorUnboltCommand, commandData.PINCode))
{
// appclusters.pdf 5.3.3.25:
// The number of seconds to wait after unlocking a lock before it automatically locks again. 0=disabled. If set, unlock
// operations from any source will be timed. For one time unlock with timeout use the specific command.
uint32_t autoRelockTime = 0;

VerifyOrReturnError(DoorLockServer::Instance().GetAutoRelockTime(commandPath.mEndpointId, autoRelockTime), true);
VerifyOrReturnError(0 != autoRelockTime, true);
DoorLockServer::Instance().ScheduleAutoRelock(commandPath.mEndpointId, autoRelockTime);
}

return true;
}

bool emberAfDoorLockClusterSetUserCallback(chip::app::CommandHandler * commandObj,
const chip::app::ConcreteCommandPath & commandPath,
const chip::app::Clusters::DoorLock::Commands::SetUser::DecodableType & commandData)
Expand Down
19 changes: 19 additions & 0 deletions src/app/clusters/door-lock-server/door-lock-server.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ class DoorLockServer
return GetFeatures(endpointId).Has(Feature::kUser) && SupportsAnyCredential(endpointId);
}

inline bool SupportsUnbolt(chip::EndpointId endpointId) { return GetFeatures(endpointId).Has(Feature::kUnbolt); }

bool OnFabricRemoved(chip::EndpointId endpointId, chip::FabricIndex fabricIndex);

static void DoorLockOnAutoRelockCallback(chip::System::Layer *, void * callbackContext);
Expand Down Expand Up @@ -490,6 +492,10 @@ class DoorLockServer
chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath,
const chip::app::Clusters::DoorLock::Commands::UnlockWithTimeout::DecodableType & commandData);

friend bool emberAfDoorLockClusterUnboltDoorCallback(
chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath,
const chip::app::Clusters::DoorLock::Commands::UnboltDoor::DecodableType & commandData);

friend bool emberAfDoorLockClusterSetHolidayScheduleCallback(
chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath,
const chip::app::Clusters::DoorLock::Commands::SetHolidaySchedule::DecodableType & commandData);
Expand Down Expand Up @@ -910,6 +916,19 @@ bool emberAfPluginDoorLockOnDoorLockCommand(chip::EndpointId endpointId, const O
bool emberAfPluginDoorLockOnDoorUnlockCommand(chip::EndpointId endpointId, const Optional<chip::ByteSpan> & pinCode,
OperationErrorEnum & err);

/**
* @brief User handler for UnboltDoor command (server)
*
* @param endpointId endpoint for which UnboltDoor command is called
* @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<chip::ByteSpan> & pinCode,
OperationErrorEnum & err);

/**
* @brief This callback is called when the AutoRelock timer is expired.
*
Expand Down
48 changes: 48 additions & 0 deletions src/app/tests/suites/DL_LockUnlock.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,38 @@ tests:
response:
value: 1

- label: "Verify that lock state attribute value is set to Locked"
command: "readAttribute"
attribute: "LockState"
response:
value: 1

- label: "Try to unbolt the door without PIN"
command: "UnboltDoor"
timedInteractionTimeoutMs: 10000

- label: "Verify that lock state attribute value is set to Unlocked"
command: "readAttribute"
attribute: "LockState"
response:
value: 2

- label: "Try to lock the door without a PIN"
command: "LockDoor"
timedInteractionTimeoutMs: 10000

- label: "Verify that lock state attribute value is set to Locked"
command: "readAttribute"
attribute: "LockState"
response:
value: 1

- label: "Verify that lock state attribute value is set to Locked"
command: "readAttribute"
attribute: "LockState"
response:
value: 1

- label: "Create new lock/unlock user"
command: "SetUser"
timedInteractionTimeoutMs: 10000
Expand Down Expand Up @@ -149,6 +181,22 @@ tests:
"UserIndex": null,
"Credentials": null,
}
- values:
- value:
{
"LockOperationType": 1,
"OperationSource": 7,
"UserIndex": null,
"Credentials": null,
}
- values:
- value:
{
"LockOperationType": 0,
"OperationSource": 7,
"UserIndex": null,
"Credentials": null,
}
- values:
- value:
{
Expand Down
Loading