Skip to content

Commit

Permalink
Chef doorlock sample update (#24118)
Browse files Browse the repository at this point in the history
Add attributes, features and extra commands to support adding/removing PINs
Implements basic lock logic

Can lock/unlock with default pin, can add pin to existing user and use that pin
  • Loading branch information
achaulk-goog authored and pull[bot] committed Nov 15, 2023
1 parent 4d06d9a commit 2463889
Show file tree
Hide file tree
Showing 3 changed files with 395 additions and 6 deletions.
212 changes: 210 additions & 2 deletions examples/chef/common/stubs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,227 @@
#ifdef EMBER_AF_PLUGIN_DOOR_LOCK_SERVER
#include <app/clusters/door-lock-server/door-lock-server.h>

class LockManager
{
public:
static constexpr uint32_t kNumEndpoints = 1;
static constexpr uint32_t kNumUsersPerEndpoint = 2;
static constexpr uint32_t kNumCredentialsPerEndpoint = 20;
static constexpr uint32_t kNumCredentialsPerUser = 10;
static constexpr uint32_t kMaxNameLength = 32;
static constexpr uint32_t kMaxDataLength = 16;

struct Credential
{
bool set(DlCredentialStatus status, DlCredentialType type, chip::ByteSpan newData)
{
if (newData.size() > kMaxDataLength || type != DlCredentialType::kPIN)
return false;
memcpy(data, newData.data(), newData.size());
info = EmberAfPluginDoorLockCredentialInfo{
status,
type,
chip::ByteSpan(data, newData.size()),
};
return true;
}

EmberAfPluginDoorLockCredentialInfo info = { DlCredentialStatus::kAvailable };
uint8_t data[kMaxDataLength];
};

struct User
{
void set(chip::CharSpan newName, uint32_t userId, DlUserStatus userStatus, DlUserType type, DlCredentialRule credentialRule)
{
size_t sz = std::min(sizeof(name), newName.size());
memcpy(name, newName.data(), sz);
info = EmberAfPluginDoorLockUserInfo{
chip::CharSpan(name, sz), chip::Span<const DlCredential>(), userId, userStatus, type, credentialRule,
};
}
bool addCredential(uint8_t type, uint16_t index)
{
if (info.credentials.size() == kNumCredentialsPerUser)
return false;
auto & cr = credentialMap[info.credentials.size()];
cr.CredentialType = type;
cr.CredentialIndex = index;
info.credentials = chip::Span<const DlCredential>(credentialMap, info.credentials.size() + 1);
return true;
}

EmberAfPluginDoorLockUserInfo info = { .userStatus = DlUserStatus::kAvailable };
char name[kMaxNameLength];
DlCredential credentialMap[kNumCredentialsPerUser];
};

struct Endpoint
{
chip::EndpointId id;
User users[kNumUsersPerEndpoint];
Credential credentials[kNumCredentialsPerEndpoint];
};

static LockManager & Instance()
{
static LockManager instance;
return instance;
}

LockManager() { defaultInitialize(); }

bool getUser(chip::EndpointId endpointId, uint16_t userIndex, EmberAfPluginDoorLockUserInfo & user)
{
auto ep = findEndpoint(endpointId);
if (!ep)
return false;
if (userIndex >= kNumUsersPerEndpoint)
return false;
user = ep->users[userIndex].info;
return true;
}

bool setUser(chip::EndpointId endpointId, uint16_t userIndex, chip::FabricIndex creator, chip::FabricIndex modifier,
const chip::CharSpan & userName, uint32_t uniqueId, DlUserStatus userStatus, DlUserType usertype,
DlCredentialRule credentialRule, const DlCredential * credentials, size_t totalCredentials)
{
auto ep = findEndpoint(endpointId);
if (!ep)
return false;
if (userIndex >= kNumUsersPerEndpoint || totalCredentials > kNumCredentialsPerUser)
return false;
ep->users[userIndex].set(userName, uniqueId, userStatus, usertype, credentialRule);
ep->users[userIndex].info.creationSource = DlAssetSource::kMatterIM;
ep->users[userIndex].info.createdBy = creator;
ep->users[userIndex].info.modificationSource = DlAssetSource::kMatterIM;
ep->users[userIndex].info.lastModifiedBy = modifier;
for (size_t i = 0; i < totalCredentials; i++)
ep->users[userIndex].addCredential(credentials[i].CredentialType, credentials[i].CredentialIndex);
return true;
}

bool getCredential(chip::EndpointId endpointId, uint16_t credentialIndex, DlCredentialType credentialType,
EmberAfPluginDoorLockCredentialInfo & credential)
{
auto ep = findEndpoint(endpointId);
if (!ep)
return false;
if (credentialIndex >= kNumCredentialsPerEndpoint)
return false;
if (credentialType != DlCredentialType::kPIN)
return false;
credential = ep->credentials[credentialIndex].info;
return true;
}

bool setCredential(chip::EndpointId endpointId, uint16_t credentialIndex, chip::FabricIndex creator, chip::FabricIndex modifier,
DlCredentialStatus credentialStatus, DlCredentialType credentialType, const chip::ByteSpan & credentialData)
{
auto ep = findEndpoint(endpointId);
if (!ep)
return false;
if (credentialIndex >= kNumCredentialsPerEndpoint)
return false;
if (credentialType != DlCredentialType::kPIN)
return false;
auto & credential = ep->credentials[credentialIndex];
if (!credential.set(credentialStatus, credentialType, credentialData))
return false;
credential.info.creationSource = DlAssetSource::kMatterIM;
credential.info.createdBy = creator;
credential.info.modificationSource = DlAssetSource::kMatterIM;
credential.info.lastModifiedBy = modifier;
return true;
}

bool checkPin(chip::EndpointId endpointId, const chip::Optional<chip::ByteSpan> & pinCode,
chip::app::Clusters::DoorLock::DlOperationError & err)
{
if (!pinCode.HasValue())
{
err = DlOperationError::kInvalidCredential;
return false;
}
auto ep = findEndpoint(endpointId);
if (!ep)
return false;
for (auto & pin : ep->credentials)
{
if (pin.info.status == DlCredentialStatus::kOccupied && pin.info.credentialData.data_equal(pinCode.Value()))
{
return true;
}
}
err = DlOperationError::kInvalidCredential;
return false;
}

private:
Endpoint * findEndpoint(chip::EndpointId endpointId)
{
for (auto & e : endpoints)
{
if (e.id == endpointId)
return &e;
}
return nullptr;
}

void defaultInitialize()
{
endpoints[0].id = 1;
uint8_t pin[6] = { 0x31, 0x32, 0x33, 0x34, 0x35, 0x36 };
endpoints[0].credentials[chip::to_underlying(DlCredentialType::kPin)][0].set(DlCredentialStatus::kOccupied,
DlCredentialType::kPin, chip::ByteSpan(pin));
endpoints[0].users[0].set(chip::CharSpan("default"), 1, DlUserStatus::kOccupiedEnabled, DlUserType::kUnrestrictedUser,
DlCredentialRule::kSingle);
endpoints[0].users[0].addCredential(chip::to_underlying(DlCredentialType::kPin), 1);
}

Endpoint endpoints[kNumEndpoints];
};

bool emberAfPluginDoorLockOnDoorLockCommand(chip::EndpointId endpointId, const chip::Optional<chip::ByteSpan> & pinCode,
chip::app::Clusters::DoorLock::OperationErrorEnum & err)
{
err = OperationErrorEnum::kUnspecified;
// TBD: LockManager, check pinCode, ...
return DoorLockServer::Instance().SetLockState(endpointId, DlLockState::kLocked);
}

bool emberAfPluginDoorLockOnDoorUnlockCommand(chip::EndpointId endpointId, const chip::Optional<chip::ByteSpan> & pinCode,
chip::app::Clusters::DoorLock::OperationErrorEnum & err)
{
err = OperationErrorEnum::kUnspecified;
// TBD: LockManager, check pinCode, ...
return DoorLockServer::Instance().SetLockState(endpointId, DlLockState::kUnlocked);
}

bool emberAfPluginDoorLockGetUser(chip::EndpointId endpointId, uint16_t userIndex, EmberAfPluginDoorLockUserInfo & user)
{
return LockManager::Instance().getUser(endpointId, userIndex - 1, user);
}

bool emberAfPluginDoorLockSetUser(chip::EndpointId endpointId, uint16_t userIndex, chip::FabricIndex creator,
chip::FabricIndex modifier, const chip::CharSpan & userName, uint32_t uniqueId,
DlUserStatus userStatus, DlUserType usertype, DlCredentialRule credentialRule,
const DlCredential * credentials, size_t totalCredentials)
{
return LockManager::Instance().setUser(endpointId, userIndex - 1, creator, modifier, userName, uniqueId, userStatus, usertype,
credentialRule, credentials, totalCredentials);
}

bool emberAfPluginDoorLockGetCredential(chip::EndpointId endpointId, uint16_t credentialIndex, DlCredentialType credentialType,
EmberAfPluginDoorLockCredentialInfo & credential)
{
return LockManager::Instance().getCredential(endpointId, credentialIndex - 1, credentialType, credential);
}

bool emberAfPluginDoorLockSetCredential(chip::EndpointId endpointId, uint16_t credentialIndex, chip::FabricIndex creator,
chip::FabricIndex modifier, DlCredentialStatus credentialStatus,
DlCredentialType credentialType, const chip::ByteSpan & credentialData)
{
return LockManager::Instance().setCredential(endpointId, credentialIndex - 1, creator, modifier, credentialStatus,
credentialType, credentialData);
}

#endif /* EMBER_AF_PLUGIN_DOOR_LOCK_SERVER */
85 changes: 84 additions & 1 deletion examples/chef/devices/rootnode_doorlock_aNKYAreMXE.matter
Original file line number Diff line number Diff line change
Expand Up @@ -1428,6 +1428,11 @@ server cluster DoorLock = 257 {
kHolidaySchedules = 0x800;
}

struct CredentialStruct {
CredentialTypeEnum credentialType = 0;
int16u credentialIndex = 1;
}

critical event DoorLockAlarm = 0 {
AlarmCodeEnum alarmCode = 0;
}
Expand Down Expand Up @@ -1468,9 +1473,11 @@ server cluster DoorLock = 257 {
readonly attribute nullable DlLockState lockState = 0;
readonly attribute DlLockType lockType = 1;
readonly attribute boolean actuatorEnabled = 2;
readonly attribute int16u numberOfTotalUsersSupported = 17;
readonly attribute int16u numberOfPINUsersSupported = 18;
readonly attribute int8u maxPINCodeLength = 23;
readonly attribute int8u minPINCodeLength = 24;
readonly attribute int8u numberOfCredentialsSupportedPerUser = 28;
attribute access(write: manage) int32u autoRelockTime = 35;
attribute access(write: manage) OperatingModeEnum operatingMode = 37;
readonly attribute DlSupportedOperatingModes supportedOperatingModes = 38;
Expand All @@ -1493,8 +1500,82 @@ server cluster DoorLock = 257 {
optional OCTET_STRING PINCode = 0;
}

request struct UnlockWithTimeoutRequest {
INT16U timeout = 0;
optional OCTET_STRING PINCode = 1;
}

request struct SetUserRequest {
DataOperationTypeEnum operationType = 0;
INT16U userIndex = 1;
nullable CHAR_STRING userName = 2;
nullable INT32U userUniqueID = 3;
nullable UserStatusEnum userStatus = 4;
nullable UserTypeEnum userType = 5;
nullable CredentialRuleEnum credentialRule = 6;
}

request struct GetUserRequest {
INT16U userIndex = 0;
}

request struct ClearUserRequest {
INT16U userIndex = 0;
}

request struct SetCredentialRequest {
DataOperationTypeEnum operationType = 0;
CredentialStruct credential = 1;
LONG_OCTET_STRING credentialData = 2;
nullable INT16U userIndex = 3;
nullable UserStatusEnum userStatus = 4;
nullable UserTypeEnum userType = 5;
}

request struct GetCredentialStatusRequest {
CredentialStruct credential = 0;
}

request struct ClearCredentialRequest {
nullable CredentialStruct credential = 0;
}

response struct GetUserResponse = 28 {
INT16U userIndex = 0;
nullable CHAR_STRING userName = 1;
nullable INT32U userUniqueID = 2;
nullable UserStatusEnum userStatus = 3;
nullable UserTypeEnum userType = 4;
nullable CredentialRuleEnum credentialRule = 5;
nullable CredentialStruct credentials[] = 6;
nullable fabric_idx creatorFabricIndex = 7;
nullable fabric_idx lastModifiedFabricIndex = 8;
nullable INT16U nextUserIndex = 9;
}

response struct SetCredentialResponse = 35 {
DlStatus status = 0;
nullable INT16U userIndex = 1;
nullable INT16U nextCredentialIndex = 2;
}

response struct GetCredentialStatusResponse = 37 {
boolean credentialExists = 0;
nullable INT16U userIndex = 1;
nullable fabric_idx creatorFabricIndex = 2;
nullable fabric_idx lastModifiedFabricIndex = 3;
nullable INT16U nextCredentialIndex = 4;
}

timed command LockDoor(LockDoorRequest): DefaultSuccess = 0;
timed command UnlockDoor(UnlockDoorRequest): DefaultSuccess = 1;
timed command UnlockWithTimeout(UnlockWithTimeoutRequest): DefaultSuccess = 3;
timed command access(invoke: administer) SetUser(SetUserRequest): DefaultSuccess = 26;
command access(invoke: administer) GetUser(GetUserRequest): GetUserResponse = 27;
timed command access(invoke: administer) ClearUser(ClearUserRequest): DefaultSuccess = 29;
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;
}

endpoint 0 {
Expand Down Expand Up @@ -1711,9 +1792,11 @@ endpoint 1 {
ram attribute lockState default = 1;
ram attribute lockType;
ram attribute actuatorEnabled;
ram attribute numberOfTotalUsersSupported default = 2;
ram attribute numberOfPINUsersSupported default = 2;
ram attribute maxPINCodeLength default = 10;
ram attribute minPINCodeLength default = 5;
ram attribute numberOfCredentialsSupportedPerUser default = 5;
ram attribute autoRelockTime;
ram attribute operatingMode;
ram attribute supportedOperatingModes default = 0xFFF6;
Expand All @@ -1724,7 +1807,7 @@ endpoint 1 {
callback attribute generatedCommandList;
callback attribute acceptedCommandList;
callback attribute attributeList;
ram attribute featureMap default = 0x0081;
ram attribute featureMap default = 0x0181;
ram attribute clusterRevision default = 6;
}
}
Expand Down
Loading

0 comments on commit 2463889

Please sign in to comment.