Skip to content

Commit

Permalink
Auto relocking support
Browse files Browse the repository at this point in the history
1. UnlockDoor command + AutoRelockTime timeout
2. UnlockWithTimeout command + user timeout
  • Loading branch information
truebiker committed Feb 8, 2022
1 parent 7570f2c commit 36141e9
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 5 deletions.
67 changes: 62 additions & 5 deletions src/app/clusters/door-lock-server/door-lock-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,15 @@ using namespace chip::app::Clusters::DoorLock;
static constexpr uint8_t DOOR_LOCK_SCHEDULE_MAX_HOUR = 23;
static constexpr uint8_t DOOR_LOCK_SCHEDULE_MAX_MINUTE = 59;

// emberEventControlSetDelayMS() uses uint32_t for timeout in milliseconds but doesn't accept
// values more than MAX(UINT32) / 2. This is internal value. Thus, lets limit our relock timeout
// in seconds with the appropriate maximum to ensure that delay setting won't fail.
static constexpr uint32_t DOOR_LOCK_MAX_LOCK_TIMEOUT_SEC = MAX_INT32U_VALUE / (2 * MILLISECOND_TICKS_PER_SECOND);

DoorLockServer DoorLockServer::instance;

void emberAfPluginDoorLockOnAutoRelock(chip::EndpointId endpointId);

/**********************************************************
* DoorLockServer public methods
*********************************************************/
Expand Down Expand Up @@ -90,10 +97,14 @@ bool DoorLockServer::SetLockState(chip::EndpointId endpointId, DlLockState newLo
// Schedule auto-relocking
if (success && DlLockOperationType::kUnlock == opType)
{
uint32_t autoRelockTime;
// 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;

success = GetAttribute(endpointId, Attributes::AutoRelockTime::Id, Attributes::AutoRelockTime::Get, autoRelockTime);
// TODO: Handle AutoRelockTime here
VerifyOrReturnError(GetAutoRelockTime(endpointId, autoRelockTime), false);
VerifyOrReturnError(0 != autoRelockTime, true);
ScheduleAutoRelock(endpointId, autoRelockTime);
}

return success;
Expand Down Expand Up @@ -142,6 +153,11 @@ bool DoorLockServer::SetPrivacyModeButton(chip::EndpointId endpointId, bool isEn
return SetAttribute(endpointId, Attributes::EnablePrivacyModeButton::Id, Attributes::EnablePrivacyModeButton::Set, isEnabled);
}

bool DoorLockServer::GetAutoRelockTime(chip::EndpointId endpointId, uint32_t & autoRelockTime)
{
return GetAttribute(endpointId, Attributes::AutoRelockTime::Id, Attributes::AutoRelockTime::Get, autoRelockTime);
}

bool DoorLockServer::GetNumberOfUserSupported(chip::EndpointId endpointId, uint16_t & numberOfUsersSupported)
{
return GetAttribute(endpointId, Attributes::NumberOfTotalUsersSupported::Id, Attributes::NumberOfTotalUsersSupported::Get,
Expand Down Expand Up @@ -2672,6 +2688,23 @@ void DoorLockServer::SendLockOperationEvent(chip::EndpointId endpointId, DlLockO
}
}

void DoorLockServer::ScheduleAutoRelock(chip::EndpointId endpointId, uint32_t timeoutSec)
{
emberEventControlSetInactive(&AutolockEvent);

AutolockEvent.endpoint = endpointId;
AutolockEvent.callback = emberAfPluginDoorLockOnAutoRelock;

uint32_t timeoutMs =
(DOOR_LOCK_MAX_LOCK_TIMEOUT_SEC >= timeoutSec) ? timeoutSec * MILLISECOND_TICKS_PER_SECOND : DOOR_LOCK_MAX_LOCK_TIMEOUT_SEC;
auto err = emberEventControlSetDelayMS(&AutolockEvent, timeoutMs);

if (EMBER_SUCCESS != err)
{
ChipLogError(Zcl, "Failed to schedule autorelock: timeout=%" PRIu32 ", status=0x%" PRIx8, timeoutSec, err);
}
}

template <typename T>
void DoorLockServer::SendEvent(chip::EndpointId endpointId, T & event)
{
Expand Down Expand Up @@ -2737,7 +2770,14 @@ bool emberAfDoorLockClusterUnlockDoorCallback(
if (DoorLockServer::Instance().HandleRemoteLockOperation(commandObj, commandPath, DlLockOperationType::kUnlock,
emberAfPluginDoorLockOnDoorUnlockCommand, commandData.pinCode))
{
// TODO: if AutoRelockTime is set, schedule automatic door locking
// 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;
Expand All @@ -2752,7 +2792,14 @@ bool emberAfDoorLockClusterUnlockWithTimeoutCallback(
if (DoorLockServer::Instance().HandleRemoteLockOperation(commandObj, commandPath, DlLockOperationType::kUnlock,
emberAfPluginDoorLockOnDoorUnlockCommand, commandData.pinCode))
{
// TODO: Implement door locking with given timeout
// appclusters.pdf 5.3.4.3:
// This command causes the lock device to unlock the door with a timeout parameter. After the time in seconds specified in
// the timeout field, the lock device will relock itself automatically.
// field: Timeout, type: uint16_t
uint32_t timeout = static_cast<uint32_t>(commandData.timeout);

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

return true;
Expand Down Expand Up @@ -3144,3 +3191,13 @@ emberAfPluginDoorLockSetSchedule(chip::EndpointId endpointId, uint8_t yearDayInd
{
return DlStatus::kFailure;
}

// =============================================================================
// Timer callbacks
// =============================================================================

void emberAfPluginDoorLockOnAutoRelock(chip::EndpointId endpointId)
{
emberEventControlSetInactive(&DoorLockServer::Instance().AutolockEvent);
DoorLockServer::Instance().SetLockState(endpointId, DlLockState::kLocked, DlOperationSource::kAuto);
}
13 changes: 13 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 @@ -86,6 +86,7 @@ class DoorLockServer
bool SetOneTouchLocking(chip::EndpointId endpointId, bool isEnabled);
bool SetPrivacyModeButton(chip::EndpointId endpointId, bool isEnabled);

bool GetAutoRelockTime(chip::EndpointId endpointId, uint32_t & autoRelockTime);
bool GetNumberOfUserSupported(chip::EndpointId endpointId, uint16_t & numberOfUsersSupported);
bool GetNumberOfPINCredentialsSupported(chip::EndpointId endpointId, uint16_t & numberOfPINCredentials);
bool GetNumberOfRFIDCredentialsSupported(chip::EndpointId endpointId, uint16_t & numberOfRFIDCredentials);
Expand Down Expand Up @@ -293,6 +294,14 @@ class DoorLockServer
Nullable<chip::NodeId> nodeId, LockOpCredentials * credList, size_t credListSize,
bool opSuccess = true);

/**
* @brief Schedule auto relocking with a given timeout
*
* @param endpointId endpoint where DoorLockServer is running
* @param timeoutSec timeout in seconds
*/
void ScheduleAutoRelock(chip::EndpointId endpointId, uint32_t timeoutSec);

/**
* @brief Send generic event
*
Expand Down Expand Up @@ -346,6 +355,10 @@ class DoorLockServer
chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath,
const chip::app::Clusters::DoorLock::Commands::UnlockWithTimeout::DecodableType & commandData);

friend void emberAfPluginDoorLockOnAutoRelock(chip::EndpointId endpointId);

EmberEventControl AutolockEvent; /**< for automatic relock scheduling */

static DoorLockServer instance;
};

Expand Down

0 comments on commit 36141e9

Please sign in to comment.