blob: ac4c8f7c03f30c830d30ed29fe3c7597ce45a3f7 [file] [log] [blame]
/*
*
* Copyright (c) 2021 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if !defined(GP_APP_DIVERSITY_POWERCYCLECOUNTING)
#error This application requires powercycle counting.
#endif
#include "powercycle_counting.h"
#include "AppConfig.h"
#include "AppEvent.h"
#include "AppTask.h"
#include "ota.h"
#include "ButtonHandler.h"
#include "StatusLed.h"
#include "qPinCfg.h"
#include <app-common/zap-generated/attributes/Accessors.h>
#include <app/clusters/on-off-server/on-off-server.h>
#include <app/server/Server.h>
using namespace ::chip;
using namespace ::chip::app;
using namespace ::chip::TLV;
using namespace ::chip::Credentials;
using namespace ::chip::DeviceLayer;
#include <platform/CHIPDeviceLayer.h>
#define QPG_LOCK_ENDPOINT_ID (1)
const uint8_t StatusLedGpios[] = QPINCFG_STATUS_LED;
const ButtonConfig_t buttons[] = QPINCFG_BUTTONS;
AppTask AppTask::sAppTask;
CHIP_ERROR AppTask::Init()
{
// Initialize common code in base class
CHIP_ERROR err = BaseAppTask::Init();
if (err != CHIP_NO_ERROR)
{
ChipLogError(NotSpecified, "BaseAppTask::Init() failed");
return err;
}
// Init ZCL Data Model and start server
TEMPORARY_RETURN_IGNORED PlatformMgr().ScheduleWork(AppTask::InitServerWrapper, 0);
// Setup powercycle reset expired handler
gpAppFramework_SetResetExpiredHandler(AppTask::PowerCycleExpiredHandlerWrapper);
// Setup button handler
ButtonHandler_Init(buttons, Q_ARRAY_SIZE(buttons), BUTTON_LOW, AppTask::ButtonEventHandlerWrapper);
// Setup Bolt
err = BoltLockMgr().Init();
if (err != CHIP_NO_ERROR)
{
ChipLogError(NotSpecified, "BoltLockMgr().Init() failed");
return err;
}
BoltLockMgr().SetCallbacks(ActionInitiated, ActionCompleted);
StatusLed_SetLed(LOCK_STATE_LED, !BoltLockMgr().IsUnlocked());
return err;
}
void AppTask::JammedLockEventHandler(AppEvent * aEvent)
{
TEMPORARY_RETURN_IGNORED SystemLayer().ScheduleLambda([] {
bool retVal;
retVal = DoorLockServer::Instance().SendLockAlarmEvent(QPG_LOCK_ENDPOINT_ID, AlarmCodeEnum::kLockJammed);
if (!retVal)
{
ChipLogProgress(NotSpecified, "[BTN] Lock jammed event send failed");
}
else
{
ChipLogProgress(NotSpecified, "[BTN] Lock jammed event sent");
}
});
}
void AppTask::LockActionEventHandler(AppEvent * aEvent)
{
bool initiated = false;
BoltLockManager::Action_t action;
int32_t actor;
CHIP_ERROR err = CHIP_NO_ERROR;
if (aEvent->Type == AppEvent::kEventType_Lock)
{
action = static_cast<BoltLockManager::Action_t>(aEvent->LockEvent.Action);
actor = aEvent->LockEvent.Actor;
}
else if (aEvent->Type == AppEvent::kEventType_Button)
{
if (BoltLockMgr().IsUnlocked())
{
action = BoltLockManager::LOCK_ACTION;
}
else
{
action = BoltLockManager::UNLOCK_ACTION;
}
actor = AppEvent::kEventType_Button;
}
else
{
err = CHIP_ERROR_INTERNAL;
}
if (err == CHIP_NO_ERROR)
{
initiated = BoltLockMgr().InitiateAction(actor, action);
if (!initiated)
{
ChipLogProgress(NotSpecified, "Action is already in progress or active.");
}
}
}
bool AppTask::ButtonEventHandler(uint8_t btnIdx, bool btnPressed)
{
// Call base class ButtonEventHandler
bool eventHandled = BaseAppTask::ButtonEventHandler(btnIdx, btnPressed);
if (eventHandled)
{
return true;
}
// Only go ahead if button index has a supported value
if (btnIdx != APP_LOCK_BUTTON && btnIdx != APP_LOCK_JAMMED_BUTTON)
{
return false;
}
ChipLogProgress(NotSpecified, "ButtonEventHandler %d, %d", btnIdx, btnPressed);
AppEvent button_event = {};
button_event.Type = AppEvent::kEventType_Button;
button_event.ButtonEvent.ButtonIdx = btnIdx;
button_event.ButtonEvent.Action = btnPressed;
if (btnIdx == APP_LOCK_BUTTON && btnPressed == true)
{
button_event.Handler = LockActionEventHandler;
}
else if (btnIdx == APP_LOCK_JAMMED_BUTTON && btnPressed == true)
{
button_event.Handler = JammedLockEventHandler;
}
else
{
return false;
}
sAppTask.PostEvent(&button_event);
return true;
}
void AppTask::ActionInitiated(BoltLockManager::Action_t aAction, int32_t aActor)
{
// If the action has been initiated by the lock, update the bolt lock trait
// and start flashing the LEDs rapidly to indicate action initiation.
if (aAction == BoltLockManager::LOCK_ACTION)
{
ChipLogProgress(NotSpecified, "Lock Action has been initiated");
}
else if (aAction == BoltLockManager::UNLOCK_ACTION)
{
ChipLogProgress(NotSpecified, "Unlock Action has been initiated");
}
if (aActor == AppEvent::kEventType_Button)
{
sAppTask.mSyncClusterToButtonAction = true;
}
if (aActor == AppEvent::kEventType_Lock)
{
sAppTask.mNotifyState = true;
}
StatusLed_BlinkLed(LOCK_STATE_LED, 50, 50);
}
void AppTask::ActionCompleted(BoltLockManager::Action_t aAction)
{
// if the action has been completed by the lock, update the bolt lock trait.
// Turn on the lock LED if in a LOCKED state OR
// Turn off the lock LED if in an UNLOCKED state.
if (aAction == BoltLockManager::LOCK_ACTION)
{
ChipLogProgress(NotSpecified, "Lock Action has been completed");
StatusLed_SetLed(LOCK_STATE_LED, true);
}
else if (aAction == BoltLockManager::UNLOCK_ACTION)
{
ChipLogProgress(NotSpecified, "Unlock Action has been completed");
StatusLed_SetLed(LOCK_STATE_LED, false);
}
if (sAppTask.mSyncClusterToButtonAction || sAppTask.mNotifyState)
{
sAppTask.UpdateClusterState();
sAppTask.mSyncClusterToButtonAction = false;
sAppTask.mNotifyState = false;
}
}
void AppTask::PostLockActionRequest(int32_t aActor, BoltLockManager::Action_t aAction)
{
AppEvent event;
event.Type = AppEvent::kEventType_Lock;
event.LockEvent.Actor = aActor;
event.LockEvent.Action = aAction;
event.Handler = LockActionEventHandler;
PostEvent(&event);
}
/**
* Update cluster status after application level changes
*/
void AppTask::UpdateClusterState(void)
{
using namespace chip::app::Clusters;
auto newValue = BoltLockMgr().IsUnlocked() ? DoorLock::DlLockState::kUnlocked : DoorLock::DlLockState::kLocked;
TEMPORARY_RETURN_IGNORED SystemLayer().ScheduleLambda([newValue] {
bool retVal = true;
chip::app::DataModel::Nullable<chip::app::Clusters::DoorLock::DlLockState> currentLockState;
chip::app::Clusters::DoorLock::Attributes::LockState::Get(QPG_LOCK_ENDPOINT_ID, currentLockState);
if (currentLockState.IsNull())
{
Protocols::InteractionModel::Status status = DoorLock::Attributes::LockState::Set(QPG_LOCK_ENDPOINT_ID, newValue);
if (status != Protocols::InteractionModel::Status::Success)
{
ChipLogError(NotSpecified, "ERR: updating DoorLock %x", to_underlying(status));
}
}
else
{
ChipLogProgress(NotSpecified, "Updating LockState attribute");
if (sAppTask.mSyncClusterToButtonAction)
{
retVal = DoorLockServer::Instance().SetLockState(QPG_LOCK_ENDPOINT_ID, newValue, OperationSourceEnum::kManual);
}
if (retVal && sAppTask.mNotifyState)
{
retVal = DoorLockServer::Instance().SetLockState(QPG_LOCK_ENDPOINT_ID, newValue, OperationSourceEnum::kRemote);
}
if (!retVal)
{
ChipLogError(NotSpecified, "ERR: updating DoorLock");
}
}
});
}
void AppTask::PowerCycleExpiredHandler(uint8_t resetCounts)
{
if (resetCounts >= NMBR_OF_RESETS_BLE_ADVERTISING)
{
// Open commissioning if no fabric was available
if (chip::Server::GetInstance().GetFabricTable().FabricCount() == 0)
{
ChipLogProgress(NotSpecified, "No fabrics, starting commissioning.");
AppTask::OpenCommissioning((intptr_t) 0);
}
}
}