Skip to content

Commit c66f2a0

Browse files
authored
[lighting-app] Support event handling in lighting-app (#28184)
1 parent 9d053d3 commit c66f2a0

File tree

5 files changed

+590
-1
lines changed

5 files changed

+590
-1
lines changed

examples/lighting-app/linux/BUILD.gn

+2
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ config("includes") {
3636

3737
executable("chip-lighting-app") {
3838
sources = [
39+
"LightingAppCommandDelegate.cpp",
3940
"include/CHIPProjectAppConfig.h",
4041
"main.cpp",
4142
]
@@ -45,6 +46,7 @@ executable("chip-lighting-app") {
4546
"${chip_root}/examples/lighting-app/lighting-common:lighting-manager",
4647
"${chip_root}/examples/platform/linux:app-main",
4748
"${chip_root}/src/lib",
49+
"${chip_root}/third_party/jsoncpp",
4850
]
4951

5052
if (chip_examples_enable_imgui_ui) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,353 @@
1+
/*
2+
*
3+
* Copyright (c) 2023 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 "LightingAppCommandDelegate.h"
20+
21+
#include <app-common/zap-generated/attributes/Accessors.h>
22+
#include <app/att-storage.h>
23+
#include <app/clusters/general-diagnostics-server/general-diagnostics-server.h>
24+
#include <app/clusters/software-diagnostics-server/software-diagnostics-server.h>
25+
#include <app/clusters/switch-server/switch-server.h>
26+
#include <app/server/Server.h>
27+
#include <platform/PlatformManager.h>
28+
29+
using namespace chip;
30+
using namespace chip::app;
31+
using namespace chip::app::Clusters;
32+
using namespace chip::DeviceLayer;
33+
34+
LightingAppCommandHandler * LightingAppCommandHandler::FromJSON(const char * json)
35+
{
36+
Json::Reader reader;
37+
Json::Value value;
38+
39+
if (!reader.parse(json, value))
40+
{
41+
ChipLogError(NotSpecified,
42+
"AllClusters App: Error parsing JSON with error %s:", reader.getFormattedErrorMessages().c_str());
43+
return nullptr;
44+
}
45+
46+
if (value.empty() || !value.isObject())
47+
{
48+
ChipLogError(NotSpecified, "AllClusters App: Invalid JSON command received");
49+
return nullptr;
50+
}
51+
52+
if (!value.isMember("Name") || !value["Name"].isString())
53+
{
54+
ChipLogError(NotSpecified, "AllClusters App: Invalid JSON command received: command name is missing");
55+
return nullptr;
56+
}
57+
58+
return Platform::New<LightingAppCommandHandler>(std::move(value));
59+
}
60+
61+
void LightingAppCommandHandler::HandleCommand(intptr_t context)
62+
{
63+
auto * self = reinterpret_cast<LightingAppCommandHandler *>(context);
64+
std::string name = self->mJsonValue["Name"].asString();
65+
66+
VerifyOrExit(!self->mJsonValue.empty(), ChipLogError(NotSpecified, "Invalid JSON event command received"));
67+
68+
if (name == "SoftwareFault")
69+
{
70+
self->OnSoftwareFaultEventHandler(Clusters::SoftwareDiagnostics::Events::SoftwareFault::Id);
71+
}
72+
else if (name == "HardwareFaultChange")
73+
{
74+
self->OnGeneralFaultEventHandler(Clusters::GeneralDiagnostics::Events::HardwareFaultChange::Id);
75+
}
76+
else if (name == "RadioFaultChange")
77+
{
78+
self->OnGeneralFaultEventHandler(Clusters::GeneralDiagnostics::Events::RadioFaultChange::Id);
79+
}
80+
else if (name == "NetworkFaultChange")
81+
{
82+
self->OnGeneralFaultEventHandler(Clusters::GeneralDiagnostics::Events::NetworkFaultChange::Id);
83+
}
84+
else if (name == "SwitchLatched")
85+
{
86+
uint8_t newPosition = static_cast<uint8_t>(self->mJsonValue["NewPosition"].asUInt());
87+
self->OnSwitchLatchedHandler(newPosition);
88+
}
89+
else if (name == "InitialPress")
90+
{
91+
uint8_t newPosition = static_cast<uint8_t>(self->mJsonValue["NewPosition"].asUInt());
92+
self->OnSwitchInitialPressedHandler(newPosition);
93+
}
94+
else if (name == "LongPress")
95+
{
96+
uint8_t newPosition = static_cast<uint8_t>(self->mJsonValue["NewPosition"].asUInt());
97+
self->OnSwitchLongPressedHandler(newPosition);
98+
}
99+
else if (name == "ShortRelease")
100+
{
101+
uint8_t previousPosition = static_cast<uint8_t>(self->mJsonValue["PreviousPosition"].asUInt());
102+
self->OnSwitchShortReleasedHandler(previousPosition);
103+
}
104+
else if (name == "LongRelease")
105+
{
106+
uint8_t previousPosition = static_cast<uint8_t>(self->mJsonValue["PreviousPosition"].asUInt());
107+
self->OnSwitchLongReleasedHandler(previousPosition);
108+
}
109+
else if (name == "MultiPressOngoing")
110+
{
111+
uint8_t newPosition = static_cast<uint8_t>(self->mJsonValue["NewPosition"].asUInt());
112+
uint8_t count = static_cast<uint8_t>(self->mJsonValue["CurrentNumberOfPressesCounted"].asUInt());
113+
self->OnSwitchMultiPressOngoingHandler(newPosition, count);
114+
}
115+
else if (name == "MultiPressComplete")
116+
{
117+
uint8_t previousPosition = static_cast<uint8_t>(self->mJsonValue["PreviousPosition"].asUInt());
118+
uint8_t count = static_cast<uint8_t>(self->mJsonValue["TotalNumberOfPressesCounted"].asUInt());
119+
self->OnSwitchMultiPressCompleteHandler(previousPosition, count);
120+
}
121+
else if (name == "PowerOnReboot")
122+
{
123+
self->OnRebootSignalHandler(BootReasonType::kPowerOnReboot);
124+
}
125+
else if (name == "BrownOutReset")
126+
{
127+
self->OnRebootSignalHandler(BootReasonType::kBrownOutReset);
128+
}
129+
else if (name == "SoftwareWatchdogReset")
130+
{
131+
self->OnRebootSignalHandler(BootReasonType::kSoftwareWatchdogReset);
132+
}
133+
else if (name == "HardwareWatchdogReset")
134+
{
135+
self->OnRebootSignalHandler(BootReasonType::kHardwareWatchdogReset);
136+
}
137+
else if (name == "SoftwareUpdateCompleted")
138+
{
139+
self->OnRebootSignalHandler(BootReasonType::kSoftwareUpdateCompleted);
140+
}
141+
else if (name == "SoftwareReset")
142+
{
143+
self->OnRebootSignalHandler(BootReasonType::kSoftwareReset);
144+
}
145+
else
146+
{
147+
ChipLogError(NotSpecified, "Unhandled command: Should never happens");
148+
}
149+
150+
exit:
151+
Platform::Delete(self);
152+
}
153+
154+
bool LightingAppCommandHandler::IsClusterPresentOnAnyEndpoint(ClusterId clusterId)
155+
{
156+
EnabledEndpointsWithServerCluster enabledEndpoints(clusterId);
157+
158+
return (enabledEndpoints.begin() != enabledEndpoints.end());
159+
}
160+
161+
void LightingAppCommandHandler::OnRebootSignalHandler(BootReasonType bootReason)
162+
{
163+
if (ConfigurationMgr().StoreBootReason(static_cast<uint32_t>(bootReason)) == CHIP_NO_ERROR)
164+
{
165+
Server::GetInstance().GenerateShutDownEvent();
166+
PlatformMgr().ScheduleWork([](intptr_t) { PlatformMgr().StopEventLoopTask(); });
167+
}
168+
else
169+
{
170+
ChipLogError(NotSpecified, "Failed to store boot reason:%d", static_cast<uint32_t>(bootReason));
171+
}
172+
}
173+
174+
void LightingAppCommandHandler::OnGeneralFaultEventHandler(uint32_t eventId)
175+
{
176+
if (!IsClusterPresentOnAnyEndpoint(Clusters::GeneralDiagnostics::Id))
177+
return;
178+
179+
if (eventId == Clusters::GeneralDiagnostics::Events::HardwareFaultChange::Id)
180+
{
181+
GeneralFaults<kMaxHardwareFaults> previous;
182+
GeneralFaults<kMaxHardwareFaults> current;
183+
184+
using GeneralDiagnostics::HardwareFaultEnum;
185+
186+
// On Linux Simulation, set following hardware faults statically.
187+
ReturnOnFailure(previous.add(to_underlying(HardwareFaultEnum::kRadio)));
188+
ReturnOnFailure(previous.add(to_underlying(HardwareFaultEnum::kPowerSource)));
189+
190+
ReturnOnFailure(current.add(to_underlying(HardwareFaultEnum::kRadio)));
191+
ReturnOnFailure(current.add(to_underlying(HardwareFaultEnum::kSensor)));
192+
ReturnOnFailure(current.add(to_underlying(HardwareFaultEnum::kPowerSource)));
193+
ReturnOnFailure(current.add(to_underlying(HardwareFaultEnum::kUserInterfaceFault)));
194+
Clusters::GeneralDiagnosticsServer::Instance().OnHardwareFaultsDetect(previous, current);
195+
}
196+
else if (eventId == Clusters::GeneralDiagnostics::Events::RadioFaultChange::Id)
197+
{
198+
GeneralFaults<kMaxRadioFaults> previous;
199+
GeneralFaults<kMaxRadioFaults> current;
200+
201+
// On Linux Simulation, set following radio faults statically.
202+
ReturnOnFailure(previous.add(EMBER_ZCL_RADIO_FAULT_ENUM_WI_FI_FAULT));
203+
ReturnOnFailure(previous.add(EMBER_ZCL_RADIO_FAULT_ENUM_THREAD_FAULT));
204+
205+
ReturnOnFailure(current.add(EMBER_ZCL_RADIO_FAULT_ENUM_WI_FI_FAULT));
206+
ReturnOnFailure(current.add(EMBER_ZCL_RADIO_FAULT_ENUM_CELLULAR_FAULT));
207+
ReturnOnFailure(current.add(EMBER_ZCL_RADIO_FAULT_ENUM_THREAD_FAULT));
208+
ReturnOnFailure(current.add(EMBER_ZCL_RADIO_FAULT_ENUM_NFC_FAULT));
209+
Clusters::GeneralDiagnosticsServer::Instance().OnRadioFaultsDetect(previous, current);
210+
}
211+
else if (eventId == Clusters::GeneralDiagnostics::Events::NetworkFaultChange::Id)
212+
{
213+
GeneralFaults<kMaxNetworkFaults> previous;
214+
GeneralFaults<kMaxNetworkFaults> current;
215+
216+
// On Linux Simulation, set following radio faults statically.
217+
ReturnOnFailure(previous.add(to_underlying(Clusters::GeneralDiagnostics::NetworkFaultEnum::kHardwareFailure)));
218+
ReturnOnFailure(previous.add(to_underlying(Clusters::GeneralDiagnostics::NetworkFaultEnum::kNetworkJammed)));
219+
220+
ReturnOnFailure(current.add(to_underlying(Clusters::GeneralDiagnostics::NetworkFaultEnum::kHardwareFailure)));
221+
ReturnOnFailure(current.add(to_underlying(Clusters::GeneralDiagnostics::NetworkFaultEnum::kNetworkJammed)));
222+
ReturnOnFailure(current.add(to_underlying(Clusters::GeneralDiagnostics::NetworkFaultEnum::kConnectionFailed)));
223+
Clusters::GeneralDiagnosticsServer::Instance().OnNetworkFaultsDetect(previous, current);
224+
}
225+
else
226+
{
227+
ChipLogError(NotSpecified, "Unknow event ID:%d", eventId);
228+
}
229+
}
230+
231+
void LightingAppCommandHandler::OnSoftwareFaultEventHandler(uint32_t eventId)
232+
{
233+
VerifyOrReturn(eventId == Clusters::SoftwareDiagnostics::Events::SoftwareFault::Id,
234+
ChipLogError(NotSpecified, "Unknown software fault event received"));
235+
236+
if (!IsClusterPresentOnAnyEndpoint(Clusters::SoftwareDiagnostics::Id))
237+
return;
238+
239+
Clusters::SoftwareDiagnostics::Events::SoftwareFault::Type softwareFault;
240+
char threadName[kMaxThreadNameLength + 1];
241+
242+
softwareFault.id = static_cast<uint64_t>(getpid());
243+
Platform::CopyString(threadName, std::to_string(softwareFault.id).c_str());
244+
245+
softwareFault.name.SetValue(CharSpan::fromCharString(threadName));
246+
247+
std::time_t result = std::time(nullptr);
248+
// Using size of 50 as it is double the expected 25 characters "Www Mmm dd hh:mm:ss yyyy\n".
249+
char timeChar[50];
250+
if (std::strftime(timeChar, sizeof(timeChar), "%c", std::localtime(&result)))
251+
{
252+
softwareFault.faultRecording.SetValue(ByteSpan(Uint8::from_const_char(timeChar), strlen(timeChar)));
253+
}
254+
255+
Clusters::SoftwareDiagnosticsServer::Instance().OnSoftwareFaultDetect(softwareFault);
256+
}
257+
258+
void LightingAppCommandHandler::OnSwitchLatchedHandler(uint8_t newPosition)
259+
{
260+
EndpointId endpoint = 0;
261+
262+
EmberAfStatus status = Switch::Attributes::CurrentPosition::Set(endpoint, newPosition);
263+
VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, ChipLogError(NotSpecified, "Failed to set CurrentPosition attribute"));
264+
ChipLogDetail(NotSpecified, "The latching switch is moved to a new position:%d", newPosition);
265+
266+
Clusters::SwitchServer::Instance().OnSwitchLatch(endpoint, newPosition);
267+
}
268+
269+
void LightingAppCommandHandler::OnSwitchInitialPressedHandler(uint8_t newPosition)
270+
{
271+
EndpointId endpoint = 0;
272+
273+
EmberAfStatus status = Switch::Attributes::CurrentPosition::Set(endpoint, newPosition);
274+
VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, ChipLogError(NotSpecified, "Failed to set CurrentPosition attribute"));
275+
ChipLogDetail(NotSpecified, "The new position when the momentary switch starts to be pressed:%d", newPosition);
276+
277+
Clusters::SwitchServer::Instance().OnInitialPress(endpoint, newPosition);
278+
}
279+
280+
void LightingAppCommandHandler::OnSwitchLongPressedHandler(uint8_t newPosition)
281+
{
282+
EndpointId endpoint = 0;
283+
284+
EmberAfStatus status = Switch::Attributes::CurrentPosition::Set(endpoint, newPosition);
285+
VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, ChipLogError(NotSpecified, "Failed to set CurrentPosition attribute"));
286+
ChipLogDetail(NotSpecified, "The new position when the momentary switch has been pressed for a long time:%d", newPosition);
287+
288+
Clusters::SwitchServer::Instance().OnLongPress(endpoint, newPosition);
289+
}
290+
291+
void LightingAppCommandHandler::OnSwitchShortReleasedHandler(uint8_t previousPosition)
292+
{
293+
EndpointId endpoint = 0;
294+
295+
EmberAfStatus status = Switch::Attributes::CurrentPosition::Set(endpoint, 0);
296+
VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, ChipLogError(NotSpecified, "Failed to reset CurrentPosition attribute"));
297+
ChipLogDetail(NotSpecified, "The the previous value of the CurrentPosition when the momentary switch has been released:%d",
298+
previousPosition);
299+
300+
Clusters::SwitchServer::Instance().OnShortRelease(endpoint, previousPosition);
301+
}
302+
303+
void LightingAppCommandHandler::OnSwitchLongReleasedHandler(uint8_t previousPosition)
304+
{
305+
EndpointId endpoint = 0;
306+
307+
EmberAfStatus status = Switch::Attributes::CurrentPosition::Set(endpoint, 0);
308+
VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, ChipLogError(NotSpecified, "Failed to reset CurrentPosition attribute"));
309+
ChipLogDetail(NotSpecified,
310+
"The the previous value of the CurrentPosition when the momentary switch has been released after having been "
311+
"pressed for a long time:%d",
312+
previousPosition);
313+
314+
Clusters::SwitchServer::Instance().OnLongRelease(endpoint, previousPosition);
315+
}
316+
317+
void LightingAppCommandHandler::OnSwitchMultiPressOngoingHandler(uint8_t newPosition, uint8_t count)
318+
{
319+
EndpointId endpoint = 0;
320+
321+
EmberAfStatus status = Switch::Attributes::CurrentPosition::Set(endpoint, newPosition);
322+
VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, ChipLogError(NotSpecified, "Failed to set CurrentPosition attribute"));
323+
ChipLogDetail(NotSpecified, "The new position when the momentary switch has been pressed in a multi-press sequence:%d",
324+
newPosition);
325+
ChipLogDetail(NotSpecified, "%d times the momentary switch has been pressed", count);
326+
327+
Clusters::SwitchServer::Instance().OnMultiPressOngoing(endpoint, newPosition, count);
328+
}
329+
330+
void LightingAppCommandHandler::OnSwitchMultiPressCompleteHandler(uint8_t previousPosition, uint8_t count)
331+
{
332+
EndpointId endpoint = 0;
333+
334+
EmberAfStatus status = Switch::Attributes::CurrentPosition::Set(endpoint, 0);
335+
VerifyOrReturn(EMBER_ZCL_STATUS_SUCCESS == status, ChipLogError(NotSpecified, "Failed to reset CurrentPosition attribute"));
336+
ChipLogDetail(NotSpecified, "The previous position when the momentary switch has been pressed in a multi-press sequence:%d",
337+
previousPosition);
338+
ChipLogDetail(NotSpecified, "%d times the momentary switch has been pressed", count);
339+
340+
Clusters::SwitchServer::Instance().OnMultiPressComplete(endpoint, previousPosition, count);
341+
}
342+
343+
void LightingAppCommandDelegate::OnEventCommandReceived(const char * json)
344+
{
345+
auto handler = LightingAppCommandHandler::FromJSON(json);
346+
if (nullptr == handler)
347+
{
348+
ChipLogError(NotSpecified, "AllClusters App: Unable to instantiate a command handler");
349+
return;
350+
}
351+
352+
chip::DeviceLayer::PlatformMgr().ScheduleWork(LightingAppCommandHandler::HandleCommand, reinterpret_cast<intptr_t>(handler));
353+
}

0 commit comments

Comments
 (0)