Skip to content

Commit 2a70eea

Browse files
Implement TC-SWTCH-2.4 and all-clusters button simulator
- Implement TC-SWTCH-2.4 - Implement action switch button simulator to drive the test Testing done: - New test (TC-SWTCH-2.4) works using the button simulator
1 parent ff679e9 commit 2a70eea

File tree

6 files changed

+825
-10
lines changed

6 files changed

+825
-10
lines changed

examples/all-clusters-app/linux/AllClustersCommandDelegate.cpp

+132
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,144 @@
2929
#include <platform/PlatformManager.h>
3030

3131
#include <air-quality-instance.h>
32+
#include "ButtonEventsSimulator.h"
3233
#include <dishwasher-mode.h>
3334
#include <laundry-washer-mode.h>
3435
#include <operational-state-delegate-impl.h>
3536
#include <oven-modes.h>
3637
#include <oven-operational-state-delegate.h>
3738
#include <rvc-modes.h>
3839

40+
#include <memory>
3941
#include <string>
42+
#include <utility>
4043

4144
using namespace chip;
4245
using namespace chip::app;
4346
using namespace chip::app::Clusters;
4447
using namespace chip::DeviceLayer;
4548

49+
namespace
50+
{
51+
52+
std::unique_ptr<ButtonEventsSimulator> sButtonSimulatorInstance{nullptr};
53+
54+
bool HasNumericField(Json::Value & jsonValue, const std::string& field)
55+
{
56+
return jsonValue.isMember(field) && jsonValue[field].isNumeric();
57+
}
58+
59+
/**
60+
* Named pipe handler for simulated long press on an action switch.
61+
*
62+
* Usage example:
63+
* echo '{"Name": "SimulateActionSwitchLongPress", "EndpointId": 3, "ButtonId": 1, "LongPressDelayMillis": 800, "LongPressDurationMillis": 1000}' > /tmp/chip_all_clusters_fifo_1146610
64+
*
65+
* JSON Arguments:
66+
* - "Name": Must be "SimulateActionSwitchLongPress"
67+
* - "EndpointId": number of endpoint having a switch cluster
68+
* - "ButtonId": switch position in the switch cluster for "down" button (not idle)
69+
* - "LongPressDelayMillis": Time in milliseconds before the LongPress
70+
* - "LongPressDurationMillis": Total duration in milliseconds from start of the press to LongRelease
71+
*
72+
* @param jsonValue - JSON payload from named pipe
73+
*/
74+
void HandleSimulateActionSwitchLongPress(Json::Value & jsonValue)
75+
{
76+
if (sButtonSimulatorInstance != nullptr)
77+
{
78+
ChipLogError(NotSpecified, "Button simulation already in progress! Ignoring request.");
79+
return;
80+
}
81+
82+
bool hasEndpointId = HasNumericField(jsonValue, "EndpointId");
83+
bool hasButtonId = HasNumericField(jsonValue, "ButtonId");
84+
bool hasLongPressDelayMillis = HasNumericField(jsonValue, "LongPressDelayMillis");
85+
bool hasLongPressDurationMillis = HasNumericField(jsonValue, "LongPressDurationMillis");
86+
if (!hasEndpointId || !hasButtonId || !hasLongPressDelayMillis || !hasLongPressDurationMillis)
87+
{
88+
std::string inputJson = jsonValue.toStyledString();
89+
ChipLogError(NotSpecified, "Missing or invalid value for one of EndpointId, ButtonId, LongPressDelayMillis or LongPressDurationMillis in %s", inputJson.c_str());
90+
return;
91+
}
92+
93+
EndpointId endpointId = static_cast<EndpointId>(jsonValue["EndpointId"].asUInt());
94+
uint8_t buttonId = static_cast<uint8_t>(jsonValue["ButtonId"].asUInt());
95+
System::Clock::Milliseconds32 longPressDelayMillis{static_cast<unsigned>(jsonValue["LongPressDelayMillis"].asUInt())};
96+
System::Clock::Milliseconds32 longPressDurationMillis{static_cast<unsigned>(jsonValue["LongPressDurationMillis"].asUInt())};
97+
auto buttonSimulator = std::make_unique<ButtonEventsSimulator>();
98+
99+
bool success = buttonSimulator->SetMode(ButtonEventsSimulator::Mode::kModeLongPress).SetLongPressDelayMillis(longPressDelayMillis)
100+
.SetLongPressDurationMillis(longPressDurationMillis).SetIdleButtonId(0).SetPressedButtonId(buttonId).SetEndpointId(endpointId)
101+
.Execute([]() { sButtonSimulatorInstance.reset(); });
102+
103+
if (!success)
104+
{
105+
ChipLogError(NotSpecified, "Failed to start execution of button simulator!");
106+
return;
107+
}
108+
109+
sButtonSimulatorInstance = std::move(buttonSimulator);
110+
}
111+
112+
/**
113+
* Named pipe handler for simulated multi-press on an action switch.
114+
*
115+
* Usage example:
116+
* echo '{"Name": "SimulateActionSwitchMultiPress", "EndpointId": 3, "ButtonId": 1, "MultiPressPressedTimeMillis": 100, "MultiPressReleasedTimeMillis": 350, "MultiPressNumPresses": 2}' > /tmp/chip_all_clusters_fifo_1146610
117+
*
118+
* JSON Arguments:
119+
* - "Name": Must be "SimulateActionSwitchMultiPress"
120+
* - "EndpointId": number of endpoint having a switch cluster
121+
* - "ButtonId": switch position in the switch cluster for "down" button (not idle)
122+
* - "MultiPressPressedTimeMillis": Pressed time in milliseconds for each press
123+
* - "MultiPressReleasedTimeMillis": Released time in milliseconds after each press
124+
* - "MultiPressNumPresses": Number of presses to simulate
125+
*
126+
* @param jsonValue - JSON payload from named pipe
127+
*/
128+
void HandleSimulateActionSwitchMultiPress(Json::Value & jsonValue)
129+
{
130+
if (sButtonSimulatorInstance != nullptr)
131+
{
132+
ChipLogError(NotSpecified, "Button simulation already in progress! Ignoring request.");
133+
return;
134+
}
135+
136+
bool hasEndpointId = HasNumericField(jsonValue, "EndpointId");
137+
bool hasButtonId = HasNumericField(jsonValue, "ButtonId");
138+
bool hasMultiPressPressedTimeMillis = HasNumericField(jsonValue, "MultiPressPressedTimeMillis");
139+
bool hasMultiPressReleasedTimeMillis = HasNumericField(jsonValue, "MultiPressReleasedTimeMillis");
140+
bool hasMultiPressNumPresses = HasNumericField(jsonValue, "MultiPressNumPresses");
141+
if (!hasEndpointId || !hasButtonId || !hasMultiPressPressedTimeMillis || !hasMultiPressReleasedTimeMillis || !hasMultiPressNumPresses)
142+
{
143+
std::string inputJson = jsonValue.toStyledString();
144+
ChipLogError(NotSpecified, "Missing or invalid value for one of EndpointId, ButtonId, MultiPressPressedTimeMillis, MultiPressReleasedTimeMillis or MultiPressNumPresses in %s", inputJson.c_str());
145+
return;
146+
}
147+
148+
EndpointId endpointId = static_cast<EndpointId>(jsonValue["EndpointId"].asUInt());
149+
uint8_t buttonId = static_cast<uint8_t>(jsonValue["ButtonId"].asUInt());
150+
System::Clock::Milliseconds32 multiPressPressedTimeMillis{static_cast<unsigned>(jsonValue["MultiPressPressedTimeMillis"].asUInt())};
151+
System::Clock::Milliseconds32 multiPressReleasedTimeMillis{static_cast<unsigned>(jsonValue["MultiPressReleasedTimeMillis"].asUInt())};
152+
uint8_t multiPressNumPresses = static_cast<uint8_t>(jsonValue["MultiPressNumPresses"].asUInt());
153+
auto buttonSimulator = std::make_unique<ButtonEventsSimulator>();
154+
155+
bool success = buttonSimulator->SetMode(ButtonEventsSimulator::Mode::kModeMultiPress).SetMultiPressPressedTimeMillis(multiPressPressedTimeMillis)
156+
.SetMultiPressReleasedTimeMillis(multiPressReleasedTimeMillis).SetMultiPressNumPresses(multiPressNumPresses).SetIdleButtonId(0).SetPressedButtonId(buttonId).SetEndpointId(endpointId)
157+
.Execute([]() { sButtonSimulatorInstance.reset(); });
158+
159+
if (!success)
160+
{
161+
ChipLogError(NotSpecified, "Failed to start execution of button simulator!");
162+
return;
163+
}
164+
165+
sButtonSimulatorInstance = std::move(buttonSimulator);
166+
}
167+
168+
} // namespace
169+
46170
AllClustersAppCommandHandler * AllClustersAppCommandHandler::FromJSON(const char * json)
47171
{
48172
Json::Reader reader;
@@ -190,6 +314,14 @@ void AllClustersAppCommandHandler::HandleCommand(intptr_t context)
190314
std::string operation = self->mJsonValue["Operation"].asString();
191315
self->OnOperationalStateChange(device, operation, self->mJsonValue["Param"]);
192316
}
317+
else if (name == "SimulateActionSwitchLongPress")
318+
{
319+
HandleSimulateActionSwitchLongPress(self->mJsonValue);
320+
}
321+
else if (name == "SimulateActionSwitchMultiPress")
322+
{
323+
HandleSimulateActionSwitchMultiPress(self->mJsonValue);
324+
}
193325
else
194326
{
195327
ChipLogError(NotSpecified, "Unhandled command: Should never happens");

examples/all-clusters-app/linux/BUILD.gn

+3
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,10 @@ source_set("chip-all-clusters-common") {
6666
"${chip_root}/examples/energy-management-app/energy-management-common/src/device-energy-management-mode.cpp",
6767
"${chip_root}/examples/energy-management-app/energy-management-common/src/energy-evse-mode.cpp",
6868
"AllClustersCommandDelegate.cpp",
69+
"AllClustersCommandDelegate.h",
6970
"AppOptions.cpp",
71+
"ButtonEventsSimulator.cpp",
72+
"ButtonEventsSimulator.h",
7073
"ValveControlDelegate.cpp",
7174
"WindowCoveringManager.cpp",
7275
"include/tv-callbacks.cpp",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
/*
2+
*
3+
* Copyright (c) 2024 Project CHIP Authors
4+
* All rights reserved.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
#include "ButtonEventsSimulator.h"
20+
21+
#include <utility>
22+
#include <functional>
23+
#include <inttypes.h>
24+
#include <stdint.h>
25+
26+
#include <app-common/zap-generated/attributes/Accessors.h>
27+
#include <app-common/zap-generated/cluster-objects.h>
28+
#include <app/EventLogging.h>
29+
#include <lib/core/CHIPError.h>
30+
#include <lib/core/DataModelTypes.h>
31+
#include <lib/support/CodeUtils.h>
32+
#include <lib/support/logging/CHIPLogging.h>
33+
#include <platform/CHIPDeviceLayer.h>
34+
#include <system/SystemLayer.h>
35+
#include <system/SystemClock.h>
36+
37+
namespace chip {
38+
namespace app {
39+
40+
namespace {
41+
42+
void SetButtonPosition(EndpointId endpointId, uint8_t position)
43+
{
44+
Clusters::Switch::Attributes::CurrentPosition::Set(endpointId, position);
45+
}
46+
47+
void EmitInitialPress(EndpointId endpointId, uint8_t newPosition)
48+
{
49+
Clusters::Switch::Events::InitialPress::Type event{newPosition};
50+
EventNumber eventNumber = 0;
51+
52+
CHIP_ERROR err = LogEvent(event, endpointId, eventNumber);
53+
if (err != CHIP_NO_ERROR)
54+
{
55+
ChipLogError(NotSpecified, "Failed to log InitialPress event: %" CHIP_ERROR_FORMAT, err.Format());
56+
}
57+
else
58+
{
59+
ChipLogProgress(NotSpecified, "Logged InitialPress(%" PRIu8 ") with ID %" PRIu64 " on Endpoint %" PRIu16,
60+
newPosition, eventNumber, endpointId);
61+
}
62+
}
63+
64+
void EmitLongPress(EndpointId endpointId, uint8_t newPosition)
65+
{
66+
Clusters::Switch::Events::LongPress::Type event{newPosition};
67+
EventNumber eventNumber = 0;
68+
69+
CHIP_ERROR err = LogEvent(event, endpointId, eventNumber);
70+
if (err != CHIP_NO_ERROR)
71+
{
72+
ChipLogError(NotSpecified, "Failed to log LongPress event: %" CHIP_ERROR_FORMAT, err.Format());
73+
}
74+
else
75+
{
76+
ChipLogProgress(NotSpecified, "Logged LongPress(%" PRIu8 ") with ID %" PRIu64 " on Endpoint %" PRIu16,
77+
newPosition, eventNumber, endpointId);
78+
}
79+
}
80+
81+
void EmitLongRelease(EndpointId endpointId, uint8_t previousPosition)
82+
{
83+
Clusters::Switch::Events::LongRelease::Type event{};
84+
event.previousPosition = previousPosition;
85+
EventNumber eventNumber = 0;
86+
87+
CHIP_ERROR err = LogEvent(event, endpointId, eventNumber);
88+
if (err != CHIP_NO_ERROR)
89+
{
90+
ChipLogError(NotSpecified, "Failed to log LongRelease event: %" CHIP_ERROR_FORMAT, err.Format());
91+
}
92+
else
93+
{
94+
ChipLogProgress(NotSpecified, "Logged LongRelease with ID %" PRIu64 " on Endpoint %" PRIu16,
95+
eventNumber, endpointId);
96+
}
97+
}
98+
99+
void EmitMultiPressComplete(EndpointId endpointId, uint8_t previousPosition, uint8_t count)
100+
{
101+
Clusters::Switch::Events::MultiPressComplete::Type event{};
102+
event.previousPosition = previousPosition;
103+
event.totalNumberOfPressesCounted = count;
104+
EventNumber eventNumber = 0;
105+
106+
CHIP_ERROR err = LogEvent(event, endpointId, eventNumber);
107+
if (err != CHIP_NO_ERROR)
108+
{
109+
ChipLogError(NotSpecified, "Failed to log MultiPressComplete event: %" CHIP_ERROR_FORMAT, err.Format());
110+
}
111+
else
112+
{
113+
ChipLogProgress(NotSpecified, "Logged MultiPressComplete(count=%" PRIu8 ") with ID %" PRIu64 " on Endpoint %" PRIu16,
114+
count, eventNumber, endpointId);
115+
}
116+
}
117+
118+
} // namespace
119+
120+
void ButtonEventsSimulator::OnTimerDone(System::Layer * layer, void * appState)
121+
{
122+
ButtonEventsSimulator *that = reinterpret_cast<ButtonEventsSimulator*>(appState);
123+
that->Next();
124+
}
125+
126+
bool ButtonEventsSimulator::Execute(DoneCallback && doneCallback)
127+
{
128+
VerifyOrReturnValue(mIdleButtonId != mPressedButtonId, false);
129+
130+
switch(mMode)
131+
{
132+
case Mode::kModeLongPress:
133+
VerifyOrReturnValue(mLongPressDurationMillis > mLongPressDelayMillis, false);
134+
SetState(State::kEmitStartOfLongPress);
135+
break;
136+
case Mode::kModeMultiPress:
137+
VerifyOrReturnValue(mMultiPressPressedTimeMillis.count() > 0, false);
138+
VerifyOrReturnValue(mMultiPressReleasedTimeMillis.count() > 0, false);
139+
VerifyOrReturnValue(mMultiPressNumPresses > 0, false);
140+
SetState(State::kEmitStartOfMultiPress);
141+
break;
142+
default:
143+
return false;
144+
}
145+
mDoneCallback = std::move(doneCallback);
146+
Next();
147+
return true;
148+
}
149+
150+
void ButtonEventsSimulator::SetState(ButtonEventsSimulator::State newState)
151+
{
152+
ButtonEventsSimulator::State oldState = mState;
153+
if (oldState != newState)
154+
{
155+
ChipLogProgress(NotSpecified, "ButtonEventsSimulator state change %u -> %u", static_cast<unsigned>(oldState), static_cast<unsigned>(newState));
156+
}
157+
158+
mState = newState;
159+
}
160+
161+
void ButtonEventsSimulator::StartTimer(System::Clock::Timeout duration)
162+
{
163+
chip::DeviceLayer::SystemLayer().StartTimer(duration, &ButtonEventsSimulator::OnTimerDone, this);
164+
}
165+
166+
void ButtonEventsSimulator::Next()
167+
{
168+
switch (mState) {
169+
case ButtonEventsSimulator::State::kIdle: {
170+
ChipLogError(NotSpecified, "Found idle state where not expected!");
171+
break;
172+
}
173+
case ButtonEventsSimulator::State::kEmitStartOfLongPress: {
174+
SetButtonPosition(mEndpointId, mPressedButtonId);
175+
EmitInitialPress(mEndpointId, mPressedButtonId);
176+
SetState(ButtonEventsSimulator::State::kEmitLongPress);
177+
StartTimer(mLongPressDelayMillis);
178+
break;
179+
}
180+
case ButtonEventsSimulator::State::kEmitLongPress: {
181+
EmitLongPress(mEndpointId, mPressedButtonId);
182+
SetState(ButtonEventsSimulator::State::kEmitLongRelease);
183+
StartTimer(mLongPressDurationMillis - mLongPressDelayMillis);
184+
break;
185+
}
186+
case ButtonEventsSimulator::State::kEmitLongRelease: {
187+
SetButtonPosition(mEndpointId, mIdleButtonId);
188+
EmitLongRelease(mEndpointId, mPressedButtonId);
189+
SetState(ButtonEventsSimulator::State::kIdle);
190+
mDoneCallback();
191+
break;
192+
}
193+
case ButtonEventsSimulator::State::kEmitStartOfMultiPress: {
194+
EmitInitialPress(mEndpointId, mPressedButtonId);
195+
StartTimer(mMultiPressNumPresses * (mMultiPressPressedTimeMillis + mMultiPressReleasedTimeMillis));
196+
SetState(ButtonEventsSimulator::State::kEmitEndOfMultiPress);
197+
break;
198+
}
199+
case ButtonEventsSimulator::State::kEmitEndOfMultiPress: {
200+
EmitMultiPressComplete(mEndpointId, mPressedButtonId, mMultiPressNumPresses);
201+
SetState(ButtonEventsSimulator::State::kIdle);
202+
mDoneCallback();
203+
break;
204+
}
205+
}
206+
}
207+
208+
} // namespace app
209+
} // namespace chip

0 commit comments

Comments
 (0)