Skip to content

Commit

Permalink
[#19807] Support schedule-restricted users in the lock-app
Browse files Browse the repository at this point in the history
  • Loading branch information
Morozov-5F committed Jul 28, 2022
1 parent cae7d71 commit 85b3f43
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 22 deletions.
3 changes: 3 additions & 0 deletions examples/lock-app/linux/include/LockEndpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ class LockEndpoint
bool setLockState(DlLockState lockState, const Optional<chip::ByteSpan> & pin, DlOperationError & err);
const char * lockStateToString(DlLockState lockState) const;

bool weekDayScheduleInAction(uint16_t userIndex) const;
bool yearDayScheduleInAction(uint16_t userIndex) const;

chip::EndpointId mEndpointId;
DlLockState mLockState;
DlDoorState mDoorState;
Expand Down
138 changes: 118 additions & 20 deletions examples/lock-app/linux/src/LockEndpoint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ bool LockEndpoint::SetDoorState(DlDoorState newState)

bool LockEndpoint::SendLockJammedAlarm() const
{
ChipLogProgress(Zcl, "Changing the Lock Jamed event [endpointId=%d]", mEndpointId);
ChipLogProgress(Zcl, "Changing the Lock Jammed event [endpointId=%d]", mEndpointId);
return DoorLockServer::Instance().SendLockAlarmEvent(mEndpointId, DlAlarmCode::kLockJammed);
}

Expand Down Expand Up @@ -370,33 +370,131 @@ bool LockEndpoint::setLockState(DlLockState lockState, const Optional<chip::Byte
return true;
}

// Check the PIN code
for (const auto & pinCredential : mLockCredentials)
// Find the credential so we can make sure it is not absent right away
auto credential = std::find_if(mLockCredentials.begin(), mLockCredentials.end(), [&pin](const LockCredentialInfo & c) {
return (c.credentialType == DlCredentialType::kPin && c.status != DlCredentialStatus::kAvailable) &&
chip::ByteSpan{ c.credentialData, c.credentialDataSize }.data_equal(pin.Value());
});
if (credential == mLockCredentials.end())
{
if (pinCredential.credentialType != DlCredentialType::kPin || pinCredential.status == DlCredentialStatus::kAvailable)
ChipLogDetail(Zcl,
"Lock App: specified PIN code was not found in the database, ignoring command to set lock state to \"%s\" "
"[endpointId=%d]",
lockStateToString(lockState), mEndpointId);

err = DlOperationError::kInvalidCredential;
return false;
}

// Find a user that correspond to this credential
auto credentialIndex = static_cast<unsigned>(credential - mLockCredentials.begin());
auto user = std::find_if(mLockUsers.begin(), mLockUsers.end(), [credential, credentialIndex](const LockUserInfo & u) {
return std::any_of(u.credentials.begin(), u.credentials.end(), [&credential, credentialIndex](const DlCredential & c) {
return c.CredentialIndex == credentialIndex && c.CredentialType == to_underlying(credential->credentialType);
});
});
if (user == mLockUsers.end())
{
ChipLogDetail(Zcl,
"Lock App: specified PIN code was found in the database, but the lock user is not associated with it "
"[endpointId=%d,credentialIndex=%u]",
mEndpointId, credentialIndex);
}

auto userIndex = static_cast<uint8_t>(user - mLockUsers.begin());

// Check if schedules affect the user
if ((user->userType == DlUserType::kScheduleRestrictedUser || user->userType == DlUserType::kWeekDayScheduleUser) &&
!weekDayScheduleInAction(userIndex))
{
if ((user->userType == DlUserType::kScheduleRestrictedUser || user->userType == DlUserType::kYearDayScheduleUser) &&
!yearDayScheduleInAction(userIndex))
{
continue;
ChipLogDetail(Zcl,
"Lock App: associated user is not allowed to operate the lock due to schedules"
"[endpointId=%d,userIndex=%u]",
mEndpointId, userIndex);
err = DlOperationError::kRestricted;
return false;
}
}

chip::ByteSpan credentialData(pinCredential.credentialData, pinCredential.credentialDataSize);
if (credentialData.data_equal(pin.Value()))
{
ChipLogDetail(
Zcl, "Lock App: specified PIN code was found in the database, setting door lock state to \"%s\" [endpointId=%d]",
lockStateToString(lockState), mEndpointId);
ChipLogDetail(
Zcl,
"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;
return true;
}
mLockState = lockState;
return true;
}

bool LockEndpoint::weekDayScheduleInAction(uint16_t userIndex) const
{
const auto & user = mLockUsers[userIndex];
if (user.userType != DlUserType::kScheduleRestrictedUser && user.userType != DlUserType::kWeekDayScheduleUser)
{
return true;
}

ChipLogDetail(Zcl,
"Lock App: specified PIN code was not found in the database, ignoring command to set lock state to \"%s\" "
"[endpointId=%d]",
lockStateToString(lockState), mEndpointId);
chip::System::Clock::Milliseconds64 cTMs;
auto chipError = chip::System::SystemClock().GetClock_RealTimeMS(cTMs);
if (chipError != CHIP_NO_ERROR)
{
ChipLogError(Zcl, "Lock App: unable to get current time to check user schedules [endpointId=%d,error=%d (%s)]", mEndpointId,
chipError.AsInteger(), chipError.AsString());
return false;
}
time_t unixEpoch = std::chrono::duration_cast<chip::System::Clock::Seconds32>(cTMs).count();

tm calendarTime{};
localtime_r(&unixEpoch, &calendarTime);

auto currentTime =
calendarTime.tm_hour * chip::kSecondsPerHour + calendarTime.tm_min * chip::kSecondsPerMinute + calendarTime.tm_sec;

// Second, check the week day schedules.
return std::any_of(
mWeekDaySchedules[userIndex].begin(), mWeekDaySchedules[userIndex].end(),
[currentTime, calendarTime](const WeekDaysScheduleInfo & s) {
auto startTime = s.schedule.startHour * chip::kSecondsPerHour + s.schedule.startMinute * chip::kSecondsPerMinute;
auto endTime = s.schedule.endHour * chip::kSecondsPerHour + s.schedule.endMinute * chip::kSecondsPerMinute;
return s.status == DlScheduleStatus::kOccupied && (to_underlying(s.schedule.daysMask) & (1 << calendarTime.tm_wday)) &&
startTime <= currentTime && currentTime <= endTime;
});
}

bool LockEndpoint::yearDayScheduleInAction(uint16_t userIndex) const
{
const auto & user = mLockUsers[userIndex];
if (user.userType != DlUserType::kScheduleRestrictedUser && user.userType != DlUserType::kYearDayScheduleUser)
{
return true;
}

chip::System::Clock::Milliseconds64 cTMs;
auto chipError = chip::System::SystemClock().GetClock_RealTimeMS(cTMs);
if (chipError != CHIP_NO_ERROR)
{
ChipLogError(Zcl, "Lock App: unable to get current time to check user schedules [endpointId=%d,error=%d (%s)]", mEndpointId,
chipError.AsInteger(), chipError.AsString());
return false;
}
auto unixEpoch = std::chrono::duration_cast<chip::System::Clock::Seconds32>(cTMs).count();
uint32_t chipEpoch = 0;
if (!chip::UnixEpochToChipEpochTime(unixEpoch, chipEpoch))
{
ChipLogError(Zcl,
"Lock App: unable to convert Unix Epoch time to Matter Epoch Time to check user schedules "
"[endpointId=%d,userIndex=%d]",
mEndpointId, userIndex);
return false;
}

err = DlOperationError::kInvalidCredential;
return false;
return std::any_of(mYearDaySchedules[userIndex].begin(), mYearDaySchedules[userIndex].end(),
[chipEpoch](const YearDayScheduleInfo & sch) {
return sch.status == DlScheduleStatus::kOccupied && sch.schedule.localStartTime <= chipEpoch &&
chipEpoch <= sch.schedule.localEndTime;
});
}

const char * LockEndpoint::lockStateToString(DlLockState lockState) const
Expand Down
8 changes: 6 additions & 2 deletions src/app/clusters/door-lock-server/door-lock-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2961,6 +2961,11 @@ DlLockDataType DoorLockServer::credentialTypeToLockDataType(DlCredentialType cre
return DlLockDataType::kUnspecified;
}

bool DoorLockServer::isUserScheduleRestricted(chip::EndpointId endpointId, const EmberAfPluginDoorLockUserInfo & user)
{
return false;
}

void DoorLockServer::setHolidayScheduleCommandHandler(chip::app::CommandHandler * commandObj,
const chip::app::ConcreteCommandPath & commandPath, uint8_t holidayIndex,
uint32_t localStartTime, uint32_t localEndTime, DlOperatingMode operatingMode)
Expand Down Expand Up @@ -3161,8 +3166,7 @@ bool DoorLockServer::HandleRemoteLockOperation(chip::app::CommandHandler * comma
// think if the app thinks that PIN is correct the door should be unlocked.
//
// [DV]: let app decide on PIN correctness, we will fail only if 'opHandler' returns false.
credentialsOk =
true; // findUserIndexByCredential(endpoint, DlCredentialType::kPin, pinCode.Value(), pinUserIdx, pinCredIdx);
credentialsOk = true;
}
else
{
Expand Down
2 changes: 2 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 @@ -292,6 +292,8 @@ class DoorLockServer

DlLockDataType credentialTypeToLockDataType(DlCredentialType credentialType);

bool isUserScheduleRestricted(chip::EndpointId endpointId, const EmberAfPluginDoorLockUserInfo & user);

void setUserCommandHandler(chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath,
const chip::app::Clusters::DoorLock::Commands::SetUser::DecodableType & commandData);

Expand Down

0 comments on commit 85b3f43

Please sign in to comment.