blob: 0122eba4336340c5d665970350000362237d00e4 [file] [log] [blame]
/*
*
* Copyright (c) 2024 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.
*/
#include "ButtonEventsSimulator.h"
#include <functional>
#include <inttypes.h>
#include <stdint.h>
#include <utility>
#include <app-common/zap-generated/attributes/Accessors.h>
#include <app-common/zap-generated/cluster-objects.h>
#include <app/EventLogging.h>
#include <lib/core/CHIPError.h>
#include <lib/core/DataModelTypes.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/logging/CHIPLogging.h>
#include <platform/CHIPDeviceLayer.h>
#include <system/SystemClock.h>
#include <system/SystemLayer.h>
namespace chip {
namespace app {
namespace {
void SetButtonPosition(EndpointId endpointId, uint8_t position)
{
Clusters::Switch::Attributes::CurrentPosition::Set(endpointId, position);
}
void EmitInitialPress(EndpointId endpointId, uint8_t newPosition)
{
Clusters::Switch::Events::InitialPress::Type event{ newPosition };
EventNumber eventNumber = 0;
CHIP_ERROR err = LogEvent(event, endpointId, eventNumber);
if (err != CHIP_NO_ERROR)
{
ChipLogError(NotSpecified, "Failed to log InitialPress event: %" CHIP_ERROR_FORMAT, err.Format());
}
else
{
ChipLogProgress(NotSpecified, "Logged InitialPress(%u) on Endpoint %u", static_cast<unsigned>(newPosition),
static_cast<unsigned>(endpointId));
}
}
void EmitLongPress(EndpointId endpointId, uint8_t newPosition)
{
Clusters::Switch::Events::LongPress::Type event{ newPosition };
EventNumber eventNumber = 0;
CHIP_ERROR err = LogEvent(event, endpointId, eventNumber);
if (err != CHIP_NO_ERROR)
{
ChipLogError(NotSpecified, "Failed to log LongPress event: %" CHIP_ERROR_FORMAT, err.Format());
}
else
{
ChipLogProgress(NotSpecified, "Logged LongPress(%u) on Endpoint %u", static_cast<unsigned>(newPosition),
static_cast<unsigned>(endpointId));
}
}
void EmitLongRelease(EndpointId endpointId, uint8_t previousPosition)
{
Clusters::Switch::Events::LongRelease::Type event{};
event.previousPosition = previousPosition;
EventNumber eventNumber = 0;
CHIP_ERROR err = LogEvent(event, endpointId, eventNumber);
if (err != CHIP_NO_ERROR)
{
ChipLogError(NotSpecified, "Failed to log LongRelease event: %" CHIP_ERROR_FORMAT, err.Format());
}
else
{
ChipLogProgress(NotSpecified, "Logged LongRelease on Endpoint %u", static_cast<unsigned>(endpointId));
}
}
void EmitMultiPressComplete(EndpointId endpointId, uint8_t previousPosition, uint8_t count)
{
Clusters::Switch::Events::MultiPressComplete::Type event{};
event.previousPosition = previousPosition;
event.totalNumberOfPressesCounted = count;
EventNumber eventNumber = 0;
CHIP_ERROR err = LogEvent(event, endpointId, eventNumber);
if (err != CHIP_NO_ERROR)
{
ChipLogError(NotSpecified, "Failed to log MultiPressComplete event: %" CHIP_ERROR_FORMAT, err.Format());
}
else
{
ChipLogProgress(NotSpecified, "Logged MultiPressComplete(count=%u) on Endpoint %u", static_cast<unsigned>(count),
static_cast<unsigned>(endpointId));
}
}
void EmitShortRelease(EndpointId endpointId, uint8_t previousPosition)
{
Clusters::Switch::Events::ShortRelease::Type event{};
event.previousPosition = previousPosition;
EventNumber eventNumber = 0;
CHIP_ERROR err = LogEvent(event, endpointId, eventNumber);
if (err != CHIP_NO_ERROR)
{
ChipLogError(NotSpecified, "Failed to log ShortRelease event: %" CHIP_ERROR_FORMAT, err.Format());
}
else
{
ChipLogProgress(NotSpecified, "Logged ShortRelease on Endpoint %u", static_cast<unsigned>(endpointId));
}
}
void EmitMultiPressOngoing(EndpointId endpointId, uint8_t newPosition, uint8_t count)
{
Clusters::Switch::Events::MultiPressOngoing::Type event{};
event.newPosition = newPosition;
event.currentNumberOfPressesCounted = count;
EventNumber eventNumber = 0;
CHIP_ERROR err = LogEvent(event, endpointId, eventNumber);
if (err != CHIP_NO_ERROR)
{
ChipLogError(NotSpecified, "Failed to log MultiPressOngoing event: %" CHIP_ERROR_FORMAT, err.Format());
}
else
{
ChipLogProgress(NotSpecified, "Logged MultiPressOngoing on Endpoint %u position %u, count %u",
static_cast<unsigned>(endpointId), newPosition, count);
}
}
} // namespace
void ButtonEventsSimulator::OnTimerDone(System::Layer * layer, void * appState)
{
ButtonEventsSimulator * that = reinterpret_cast<ButtonEventsSimulator *>(appState);
that->Next();
}
bool ButtonEventsSimulator::Execute(DoneCallback && doneCallback)
{
VerifyOrReturnValue(mIdleButtonId != mPressedButtonId, false);
switch (mMode)
{
case Mode::kModeLongPress:
VerifyOrReturnValue(mLongPressDurationMillis > mLongPressDelayMillis, false);
SetState(State::kEmitStartOfLongPress);
break;
case Mode::kModeMultiPress:
VerifyOrReturnValue(mMultiPressPressedTimeMillis.count() > 0, false);
VerifyOrReturnValue(mMultiPressReleasedTimeMillis.count() > 0, false);
VerifyOrReturnValue(mMultiPressNumPresses > 0, false);
SetState(State::kEmitStartOfMultiPress);
break;
default:
return false;
}
mDoneCallback = std::move(doneCallback);
Next();
return true;
}
void ButtonEventsSimulator::SetState(ButtonEventsSimulator::State newState)
{
ButtonEventsSimulator::State oldState = mState;
if (oldState != newState)
{
ChipLogProgress(NotSpecified, "ButtonEventsSimulator state change %u -> %u", static_cast<unsigned>(oldState),
static_cast<unsigned>(newState));
}
mState = newState;
}
void ButtonEventsSimulator::StartTimer(System::Clock::Timeout duration)
{
chip::DeviceLayer::SystemLayer().StartTimer(duration, &ButtonEventsSimulator::OnTimerDone, this);
}
void ButtonEventsSimulator::Next()
{
switch (mState)
{
case ButtonEventsSimulator::State::kIdle: {
ChipLogError(NotSpecified, "Found idle state where not expected!");
break;
}
case ButtonEventsSimulator::State::kEmitStartOfLongPress: {
SetButtonPosition(mEndpointId, mPressedButtonId);
EmitInitialPress(mEndpointId, mPressedButtonId);
SetState(ButtonEventsSimulator::State::kEmitLongPress);
StartTimer(mLongPressDelayMillis);
break;
}
case ButtonEventsSimulator::State::kEmitLongPress: {
EmitLongPress(mEndpointId, mPressedButtonId);
SetState(ButtonEventsSimulator::State::kEmitLongRelease);
StartTimer(mLongPressDurationMillis - mLongPressDelayMillis);
break;
}
case ButtonEventsSimulator::State::kEmitLongRelease: {
SetButtonPosition(mEndpointId, mIdleButtonId);
if (mFeatureMap & static_cast<uint32_t>(Clusters::Switch::Feature::kMomentarySwitchLongPress))
{
EmitLongRelease(mEndpointId, mPressedButtonId);
}
else if (mFeatureMap & static_cast<uint32_t>(Clusters::Switch::Feature::kMomentarySwitchRelease))
{
EmitShortRelease(mEndpointId, mPressedButtonId);
}
SetState(ButtonEventsSimulator::State::kIdle);
mDoneCallback();
break;
}
case ButtonEventsSimulator::State::kEmitStartOfMultiPress: {
SetButtonPosition(mEndpointId, mPressedButtonId);
EmitInitialPress(mEndpointId, mPressedButtonId);
if (mFeatureMap & static_cast<uint32_t>(Clusters::Switch::Feature::kActionSwitch))
{
StartTimer(mMultiPressNumPresses * (mMultiPressPressedTimeMillis + mMultiPressReleasedTimeMillis));
SetState(ButtonEventsSimulator::State::kEmitEndOfMultiPress);
}
else
{
SetState(ButtonEventsSimulator::State::kMultiPressButtonRelease);
StartTimer(mMultiPressPressedTimeMillis);
}
break;
}
case ButtonEventsSimulator::State::kMultiPressButtonRelease: {
++mMultiPressPressesDone;
if (mMultiPressPressesDone > 1)
{
EmitMultiPressOngoing(mEndpointId, mPressedButtonId, mMultiPressPressesDone);
}
if (mMultiPressPressesDone == mMultiPressNumPresses)
{
SetState(ButtonEventsSimulator::State::kEmitEndOfMultiPress);
}
else
{
SetState(ButtonEventsSimulator::State::kEmitStartOfMultiPress);
}
if (mFeatureMap & static_cast<uint32_t>(Clusters::Switch::Feature::kMomentarySwitchRelease))
{
EmitShortRelease(mEndpointId, mPressedButtonId);
}
SetButtonPosition(mEndpointId, mIdleButtonId);
StartTimer(mMultiPressReleasedTimeMillis);
break;
}
case ButtonEventsSimulator::State::kEmitEndOfMultiPress: {
if (mFeatureMap & static_cast<uint32_t>(Clusters::Switch::Feature::kActionSwitch) && mMultiPressNumPresses > mMultiPressMax)
{
EmitMultiPressComplete(mEndpointId, mPressedButtonId, 0);
}
else
{
EmitMultiPressComplete(mEndpointId, mPressedButtonId, mMultiPressNumPresses);
}
SetState(ButtonEventsSimulator::State::kIdle);
mDoneCallback();
break;
}
}
}
} // namespace app
} // namespace chip