Skip to content

Commit 0834f1b

Browse files
[Silabs] Closure State LCD implementation (project-chip#41521)
* closure LCD UI * Add attribute reporting updates * PR comments * PR comments * Restyled by whitespace * Restyled by clang-format * PR Comments * Addressing PR comments and reducing code bloats * remove non platfrom changes * Restyled by whitespace * Restyled by clang-format * PR comments * PR comments * PR comments * Restyled by clang-format * PR comments --------- Co-authored-by: Restyled.io <[email protected]>
1 parent 0c3358c commit 0834f1b

File tree

10 files changed

+525
-2
lines changed

10 files changed

+525
-2
lines changed

examples/closure-app/silabs/BUILD.gn

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,10 @@ silabs_executable("closure_app") {
139139
"src/DataModelCallbacks.cpp",
140140
]
141141

142+
if (!disable_lcd) {
143+
sources += [ "src/ClosureUI.cpp" ]
144+
}
145+
142146
deps = [ ":sdk" ]
143147

144148
if (wifi_soc) {

examples/closure-app/silabs/include/AppEvent.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ struct AppEvent : public BaseAppEvent
2727
{
2828
kEventType_Closure = BaseAppEvent::kEventType_Max + 1,
2929
kEventType_Install,
30+
kEventType_UpdateUI,
3031
};
3132

3233
struct

examples/closure-app/silabs/include/AppTask.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,21 @@ class AppTask : public BaseApplication
7878
*/
7979
static void ButtonEventHandler(uint8_t button, uint8_t btnAction);
8080

81+
#ifdef DISPLAY_ENABLED
82+
/**
83+
* @brief Updates the closure UI with current closure state
84+
*/
85+
static void UpdateClosureUI();
86+
87+
/**
88+
* @brief Event handler for UI update events
89+
* Called from app task context to safely update UI with chip stack locked
90+
*
91+
* @param aEvent pointer to the UI update event being processed
92+
*/
93+
static void UpdateClosureUIHandler(AppEvent * aEvent);
94+
#endif // DISPLAY_ENABLED
95+
8196
/**
8297
* @brief Closure button action event handler
8398
* Handles button press events for closure control operations

examples/closure-app/silabs/include/ClosureManager.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@
3131
#include <AppEvent.h>
3232
#include <lib/core/DataModelTypes.h>
3333

34+
#ifdef DISPLAY_ENABLED
35+
#include "ClosureUI.h"
36+
#endif
37+
3438
class ClosureManager
3539
{
3640
public:
@@ -156,6 +160,16 @@ class ClosureManager
156160
*/
157161
const Action_t & GetCurrentAction() const { return mCurrentAction; }
158162

163+
#ifdef DISPLAY_ENABLED
164+
/**
165+
* @brief Gets closure data specifically for UI display.
166+
*
167+
* @return ClosureUIData structure containing main state and overall current state
168+
*/
169+
ClosureUIData GetClosureUIData();
170+
171+
#endif // DISPLAY_ENABLED
172+
159173
/**
160174
* @brief Checks if a MoveTo action is currently in progress.
161175
*
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
*
3+
* Copyright (c) 2025 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+
#pragma once
20+
21+
#include "ClosureUIStrings.h"
22+
#include "glib.h"
23+
#include "lcd.h"
24+
#include <app-common/zap-generated/cluster-objects.h>
25+
#include <app/clusters/closure-control-server/closure-control-cluster-objects.h>
26+
#include <app/data-model/Nullable.h>
27+
28+
/**
29+
* @brief Structure to hold closure data needed for UI display
30+
*/
31+
struct ClosureUIData
32+
{
33+
chip::app::Clusters::ClosureControl::MainStateEnum mainState;
34+
chip::app::DataModel::Nullable<chip::app::Clusters::ClosureControl::GenericOverallCurrentState> overallCurrentState;
35+
};
36+
37+
struct ClosureUITextInitializer;
38+
39+
class ClosureUI
40+
{
41+
public:
42+
static void DrawUI(GLIB_Context_t * glibContext);
43+
static void SetMainState(chip::app::Clusters::ClosureControl::MainStateEnum state);
44+
45+
static void FormatAndSetPosition(const char * suffix);
46+
static void FormatAndSetLatch(const char * suffix);
47+
static void FormatAndSetSecure(const char * suffix);
48+
static void FormatAndSetSpeed(const char * suffix);
49+
50+
private:
51+
friend struct ClosureUITextInitializer;
52+
static void DrawHeader(GLIB_Context_t * glibContext);
53+
static void DrawFooter(GLIB_Context_t * glibContext);
54+
static void DrawMainState(GLIB_Context_t * glibContext);
55+
static void DrawOverallCurrentState(GLIB_Context_t * glibContext);
56+
57+
// Static variables to store the current closure state
58+
static chip::app::Clusters::ClosureControl::MainStateEnum sMainState;
59+
static char sPositionText[ClosureUIStrings::LCD_STRING_BUFFER_SIZE];
60+
static char sLatchText[ClosureUIStrings::LCD_STRING_BUFFER_SIZE];
61+
static char sSecureText[ClosureUIStrings::LCD_STRING_BUFFER_SIZE];
62+
static char sSpeedText[ClosureUIStrings::LCD_STRING_BUFFER_SIZE];
63+
static char sStateText[ClosureUIStrings::LCD_STRING_BUFFER_SIZE];
64+
};
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
*
3+
* Copyright (c) 2025 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+
#pragma once
20+
21+
#include <cstddef>
22+
23+
/**
24+
* @file ClosureUIStrings.h
25+
* @brief UI string constants
26+
27+
* @note IMPORTANT FOR DEVELOPERS:
28+
* If you modify existing strings or add new strings in this file, you MUST ensure
29+
* that all prefix + suffix combinations fit within LCD_CH_LINE_LEN (16 characters),
30+
* as the LCD display can only show 16 characters per line. All text buffers are
31+
* sized to LCD_STRING_BUFFER_SIZE (17 bytes: 16 characters + null terminator).
32+
*/
33+
34+
namespace ClosureUIStrings {
35+
36+
// Shared suffix for unknown state
37+
inline constexpr const char SUFFIX_UNKNOWN[] = "Unknown";
38+
39+
inline constexpr const char POSITION_PREFIX[] = "Pos: ";
40+
inline constexpr const char POSITION_SUFFIX_CLOSED[] = "Closed";
41+
inline constexpr const char POSITION_SUFFIX_OPEN[] = "Open";
42+
inline constexpr const char POSITION_SUFFIX_PARTIAL[] = "Partial";
43+
inline constexpr const char POSITION_SUFFIX_PEDESTRIAN[] = "Pedest";
44+
inline constexpr const char POSITION_SUFFIX_VENTILATION[] = "Ventil";
45+
inline constexpr const char POSITION_SUFFIX_SIGNATURE[] = "Sign";
46+
47+
inline constexpr const char LATCH_PREFIX[] = "Latch: ";
48+
inline constexpr const char LATCH_SUFFIX_ENGAGED[] = "Yes";
49+
inline constexpr const char LATCH_SUFFIX_RELEASED[] = "No";
50+
51+
inline constexpr const char SECURE_PREFIX[] = "Secure: ";
52+
inline constexpr const char SECURE_SUFFIX_YES[] = "Yes";
53+
inline constexpr const char SECURE_SUFFIX_NO[] = "No";
54+
55+
inline constexpr const char SPEED_PREFIX[] = "Speed: ";
56+
inline constexpr const char SPEED_SUFFIX_LOW[] = "Low";
57+
inline constexpr const char SPEED_SUFFIX_MEDIUM[] = "Med";
58+
inline constexpr const char SPEED_SUFFIX_HIGH[] = "High";
59+
inline constexpr const char SPEED_SUFFIX_AUTO[] = "Auto";
60+
61+
inline constexpr const char STATE_PREFIX[] = "State: ";
62+
inline constexpr const char STATE_SUFFIX_STOPPED[] = "Stopped";
63+
inline constexpr const char STATE_SUFFIX_MOVING[] = "Moving";
64+
inline constexpr const char STATE_SUFFIX_WAITING[] = "Waiting";
65+
inline constexpr const char STATE_SUFFIX_ERROR[] = "Error";
66+
inline constexpr const char STATE_SUFFIX_CALIBRATING[] = "Calib";
67+
inline constexpr const char STATE_SUFFIX_PROTECTED[] = "Protect";
68+
inline constexpr const char STATE_SUFFIX_DISENGAGED[] = "Diseng";
69+
inline constexpr const char STATE_SUFFIX_SETUP_REQUIRED[] = "SetupReq";
70+
71+
inline constexpr const char FOOTER_TEXT[] = "Closure App";
72+
73+
// LCD display line maximum length
74+
inline constexpr size_t LCD_CH_LINE_LEN = 16;
75+
76+
// LCD string buffer size (lcd line length + null terminator)
77+
inline constexpr size_t LCD_STRING_BUFFER_SIZE = LCD_CH_LINE_LEN + 1;
78+
} // namespace ClosureUIStrings

examples/closure-app/silabs/src/AppTask.cpp

Lines changed: 106 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
#include "LEDWidget.h"
2424

2525
#ifdef DISPLAY_ENABLED
26+
#include "ClosureUI.h"
27+
#include "ClosureUIStrings.h"
2628
#include "lcd.h"
2729
#ifdef QR_CODE_ENABLED
2830
#include "qrcodegen.h"
@@ -48,6 +50,7 @@
4850
#include <setup_payload/OnboardingCodesUtil.h>
4951
#include <setup_payload/QRCodeSetupPayloadGenerator.h>
5052
#include <setup_payload/SetupPayload.h>
53+
#include <stdio.h>
5154

5255
#define APP_FUNCTION_BUTTON 0
5356
#define APP_CLOSURE_BUTTON 1
@@ -81,14 +84,15 @@ CHIP_ERROR AppTask::AppInit()
8184

8285
#ifdef DISPLAY_ENABLED
8386
GetLCD().Init((uint8_t *) "Closure-App");
87+
GetLCD().SetCustomUI(ClosureUI::DrawUI);
8488
#endif
8589

8690
// Initialization of Closure Manager and endpoints of closure and closurepanel.
8791
ClosureManager::GetInstance().Init();
8892

8993
// Update the LCD with the Stored value. Show QR Code if not provisioned
9094
#ifdef DISPLAY_ENABLED
91-
GetLCD().WriteDemoUI(false);
95+
UpdateClosureUI();
9296
#ifdef QR_CODE_ENABLED
9397
#ifdef SL_WIFI
9498
if (!ConnectivityMgr().IsWiFiStationProvisioned())
@@ -99,7 +103,7 @@ CHIP_ERROR AppTask::AppInit()
99103
GetLCD().ShowQRCode(true);
100104
}
101105
#endif // QR_CODE_ENABLED
102-
#endif
106+
#endif // DISPLAY_ENABLED
103107

104108
return err;
105109
}
@@ -228,3 +232,103 @@ void AppTask::ClosureButtonActionEventHandler(AppEvent * aEvent)
228232
ChipLogError(AppServer, "Unhandled event type in ClosureButtonActionEventHandler");
229233
}
230234
}
235+
236+
#ifdef DISPLAY_ENABLED
237+
void AppTask::UpdateClosureUIHandler(AppEvent * aEvent)
238+
{
239+
if (aEvent->Type == AppEvent::kEventType_UpdateUI)
240+
{
241+
UpdateClosureUI();
242+
}
243+
}
244+
245+
void AppTask::UpdateClosureUI()
246+
{
247+
ClosureManager & closureManager = ClosureManager::GetInstance();
248+
249+
// Lock chip stack when accessing CHIP attributes from app task context
250+
DeviceLayer::PlatformMgr().LockChipStack();
251+
auto uiData = closureManager.GetClosureUIData();
252+
DeviceLayer::PlatformMgr().UnlockChipStack();
253+
254+
ClosureUI::SetMainState(uiData.mainState);
255+
256+
const char * positionSuffix = ClosureUIStrings::SUFFIX_UNKNOWN;
257+
if (!uiData.overallCurrentState.IsNull() && uiData.overallCurrentState.Value().position.HasValue() &&
258+
!uiData.overallCurrentState.Value().position.Value().IsNull())
259+
{
260+
switch (uiData.overallCurrentState.Value().position.Value().Value())
261+
{
262+
case chip::app::Clusters::ClosureControl::CurrentPositionEnum::kFullyClosed:
263+
positionSuffix = ClosureUIStrings::POSITION_SUFFIX_CLOSED;
264+
break;
265+
case chip::app::Clusters::ClosureControl::CurrentPositionEnum::kFullyOpened:
266+
positionSuffix = ClosureUIStrings::POSITION_SUFFIX_OPEN;
267+
break;
268+
case chip::app::Clusters::ClosureControl::CurrentPositionEnum::kPartiallyOpened:
269+
positionSuffix = ClosureUIStrings::POSITION_SUFFIX_PARTIAL;
270+
break;
271+
case chip::app::Clusters::ClosureControl::CurrentPositionEnum::kOpenedForPedestrian:
272+
positionSuffix = ClosureUIStrings::POSITION_SUFFIX_PEDESTRIAN;
273+
break;
274+
case chip::app::Clusters::ClosureControl::CurrentPositionEnum::kOpenedForVentilation:
275+
positionSuffix = ClosureUIStrings::POSITION_SUFFIX_VENTILATION;
276+
break;
277+
default:
278+
positionSuffix = ClosureUIStrings::SUFFIX_UNKNOWN;
279+
break;
280+
}
281+
}
282+
ClosureUI::FormatAndSetPosition(positionSuffix);
283+
284+
const char * latchSuffix = ClosureUIStrings::SUFFIX_UNKNOWN;
285+
if (!uiData.overallCurrentState.IsNull() && uiData.overallCurrentState.Value().latch.HasValue() &&
286+
!uiData.overallCurrentState.Value().latch.Value().IsNull())
287+
{
288+
latchSuffix = uiData.overallCurrentState.Value().latch.Value().Value() ? ClosureUIStrings::LATCH_SUFFIX_ENGAGED
289+
: ClosureUIStrings::LATCH_SUFFIX_RELEASED;
290+
}
291+
ClosureUI::FormatAndSetLatch(latchSuffix);
292+
293+
const char * secureSuffix = ClosureUIStrings::SUFFIX_UNKNOWN;
294+
if (!uiData.overallCurrentState.IsNull() && !uiData.overallCurrentState.Value().secureState.IsNull())
295+
{
296+
secureSuffix = uiData.overallCurrentState.Value().secureState.Value() ? ClosureUIStrings::SECURE_SUFFIX_YES
297+
: ClosureUIStrings::SECURE_SUFFIX_NO;
298+
}
299+
ClosureUI::FormatAndSetSecure(secureSuffix);
300+
301+
const char * speedSuffix = ClosureUIStrings::SUFFIX_UNKNOWN;
302+
if (!uiData.overallCurrentState.IsNull() && uiData.overallCurrentState.Value().speed.HasValue())
303+
{
304+
switch (uiData.overallCurrentState.Value().speed.Value())
305+
{
306+
case chip::app::Clusters::Globals::ThreeLevelAutoEnum::kLow:
307+
speedSuffix = ClosureUIStrings::SPEED_SUFFIX_LOW;
308+
break;
309+
case chip::app::Clusters::Globals::ThreeLevelAutoEnum::kMedium:
310+
speedSuffix = ClosureUIStrings::SPEED_SUFFIX_MEDIUM;
311+
break;
312+
case chip::app::Clusters::Globals::ThreeLevelAutoEnum::kHigh:
313+
speedSuffix = ClosureUIStrings::SPEED_SUFFIX_HIGH;
314+
break;
315+
case chip::app::Clusters::Globals::ThreeLevelAutoEnum::kAuto:
316+
speedSuffix = ClosureUIStrings::SPEED_SUFFIX_AUTO;
317+
break;
318+
default:
319+
speedSuffix = ClosureUIStrings::SUFFIX_UNKNOWN;
320+
break;
321+
}
322+
}
323+
ClosureUI::FormatAndSetSpeed(speedSuffix);
324+
325+
#ifdef SL_WIFI
326+
if (ConnectivityMgr().IsWiFiStationProvisioned())
327+
#else
328+
if (ConnectivityMgr().IsThreadProvisioned())
329+
#endif /* !SL_WIFI */
330+
{
331+
AppTask::GetAppTask().GetLCD().WriteDemoUI(false); // State doesn't matter for custom UI
332+
}
333+
}
334+
#endif // DISPLAY_ENABLED

examples/closure-app/silabs/src/ClosureManager.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,6 +1284,17 @@ bool ClosureManager::GetPanelNextPosition(const GenericDimensionStateStruct & cu
12841284
}
12851285
return true;
12861286
}
1287+
1288+
#ifdef DISPLAY_ENABLED
1289+
ClosureUIData ClosureManager::GetClosureUIData()
1290+
{
1291+
ClosureUIData uiData;
1292+
mClosureEndpoint1.GetLogic().GetMainState(uiData.mainState);
1293+
mClosureEndpoint1.GetLogic().GetOverallCurrentState(uiData.overallCurrentState);
1294+
return uiData;
1295+
}
1296+
#endif // DISPLAY_ENABLED
1297+
12871298
bool ClosureManager::IsClosureControlMotionInProgress() const
12881299
{
12891300
return mIsMoveToInProgress;

0 commit comments

Comments
 (0)