FastLED 3.10.8
Loading...
Searching...
No Matches
json_helpers.cpp.hpp
Go to the documentation of this file.
1// ok no header
3#include "fl/log/log.h"
4#include "fl/stl/cstdlib.h" // For strtol
5#include "fl/stl/noexcept.h"
6
7namespace fl {
8namespace wled {
9
10// Helper function to parse hex color string to RGB
11bool parseHexColor(const fl::string& hexStr, u8& r, u8& g, u8& b) FL_NO_EXCEPT {
12 fl::string hex = hexStr;
13
14 // Strip leading '#' if present
15 if (hex.length() > 0 && hex[0] == '#') {
16 hex = hex.substr(1);
17 }
18
19 // Validate length (must be exactly 6 characters)
20 if (hex.length() != 6) {
21 return false;
22 }
23
24 // Validate hex characters
25 for (size_t i = 0; i < hex.length(); i++) {
26 char c = hex[i];
27 if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
28 return false;
29 }
30 }
31
32 // Parse hex digits
33 auto hexDigitToInt = [](char c) -> u8 {
34 if (c >= '0' && c <= '9') return c - '0';
35 if (c >= 'a' && c <= 'f') return c - 'a' + 10;
36 if (c >= 'A' && c <= 'F') return c - 'A' + 10;
37 return 0;
38 };
39
40 r = (hexDigitToInt(hex[0]) << 4) | hexDigitToInt(hex[1]);
41 g = (hexDigitToInt(hex[2]) << 4) | hexDigitToInt(hex[3]);
42 b = (hexDigitToInt(hex[4]) << 4) | hexDigitToInt(hex[5]);
43
44 return true;
45}
46
47// Helper function to convert RGB to hex string
50 hex.reserve(6);
51
52 auto byteToHex = [](u8 val) -> fl::string {
53 const char hexChars[] = "0123456789ABCDEF";
55 result.push_back(hexChars[val >> 4]);
56 result.push_back(hexChars[val & 0x0F]);
57 return result;
58 };
59
60 hex += byteToHex(r);
61 hex += byteToHex(g);
62 hex += byteToHex(b);
63
64 return hex;
65}
66
67// Helper function to parse segment fields
69 // Extract basic properties
70 if (segJson.contains("start") && segJson["start"].is_int()) {
71 i64 startInt = segJson["start"] | 0;
72 if (startInt < 0) startInt = 0;
73 if (startInt > 65535) startInt = 65535;
74 seg.mStart = static_cast<u16>(startInt);
75 }
76
77 if (segJson.contains("stop") && segJson["stop"].is_int()) {
78 i64 stopInt = segJson["stop"] | 0;
79 if (stopInt < 0) stopInt = 0;
80 if (stopInt > 65535) stopInt = 65535;
81 seg.mStop = static_cast<u16>(stopInt);
82 }
83
84 if (segJson.contains("len") && segJson["len"].is_int()) {
85 i64 lenInt = segJson["len"] | 0;
86 if (lenInt < 0) lenInt = 0;
87 if (lenInt > 65535) lenInt = 65535;
88 seg.mLen = static_cast<u16>(lenInt);
89 }
90
91 if (segJson.contains("grp") && segJson["grp"].is_int()) {
92 i64 grpInt = segJson["grp"] | 1;
93 if (grpInt < 1) grpInt = 1;
94 if (grpInt > 255) grpInt = 255;
95 seg.mGrp = static_cast<u8>(grpInt);
96 }
97
98 if (segJson.contains("spc") && segJson["spc"].is_int()) {
99 i64 spcInt = segJson["spc"] | 0;
100 if (spcInt < 0) spcInt = 0;
101 if (spcInt > 255) spcInt = 255;
102 seg.mSpc = static_cast<u8>(spcInt);
103 }
104
105 if (segJson.contains("of") && segJson["of"].is_int()) {
106 i64 ofInt = segJson["of"] | 0;
107 if (ofInt < 0) ofInt = 0;
108 if (ofInt > 65535) ofInt = 65535;
109 seg.mOf = static_cast<u16>(ofInt);
110 }
111
112 if (segJson.contains("on") && segJson["on"].is_bool()) {
113 seg.mOn = segJson["on"] | true;
114 }
115
116 if (segJson.contains("bri") && segJson["bri"].is_int()) {
117 i64 briInt = segJson["bri"] | 255;
118 if (briInt < 0) briInt = 0;
119 if (briInt > 255) briInt = 255;
120 seg.mBri = static_cast<u8>(briInt);
121 }
122
123 if (segJson.contains("cct") && segJson["cct"].is_int()) {
124 i64 cctInt = segJson["cct"] | 0;
125 if (cctInt < 0) cctInt = 0;
126 if (cctInt > 65535) cctInt = 65535;
127 seg.mCct = static_cast<u16>(cctInt);
128 }
129
130 // Effect properties
131 if (segJson.contains("fx") && segJson["fx"].is_int()) {
132 i64 fxInt = segJson["fx"] | 0;
133 if (fxInt < 0) fxInt = 0;
134 if (fxInt > 255) fxInt = 255;
135 seg.mFx = static_cast<u8>(fxInt);
136 }
137
138 if (segJson.contains("sx") && segJson["sx"].is_int()) {
139 i64 sxInt = segJson["sx"] | 128;
140 if (sxInt < 0) sxInt = 0;
141 if (sxInt > 255) sxInt = 255;
142 seg.mSx = static_cast<u8>(sxInt);
143 }
144
145 if (segJson.contains("ix") && segJson["ix"].is_int()) {
146 i64 ixInt = segJson["ix"] | 128;
147 if (ixInt < 0) ixInt = 0;
148 if (ixInt > 255) ixInt = 255;
149 seg.mIx = static_cast<u8>(ixInt);
150 }
151
152 if (segJson.contains("pal") && segJson["pal"].is_int()) {
153 i64 palInt = segJson["pal"] | 0;
154 if (palInt < 0) palInt = 0;
155 if (palInt > 255) palInt = 255;
156 seg.mPal = static_cast<u8>(palInt);
157 }
158
159 if (segJson.contains("c1") && segJson["c1"].is_int()) {
160 i64 c1Int = segJson["c1"] | 128;
161 if (c1Int < 0) c1Int = 0;
162 if (c1Int > 255) c1Int = 255;
163 seg.mC1 = static_cast<u8>(c1Int);
164 }
165
166 if (segJson.contains("c2") && segJson["c2"].is_int()) {
167 i64 c2Int = segJson["c2"] | 128;
168 if (c2Int < 0) c2Int = 0;
169 if (c2Int > 255) c2Int = 255;
170 seg.mC2 = static_cast<u8>(c2Int);
171 }
172
173 if (segJson.contains("c3") && segJson["c3"].is_int()) {
174 i64 c3Int = segJson["c3"] | 16;
175 if (c3Int < 0) c3Int = 0;
176 if (c3Int > 255) c3Int = 255;
177 seg.mC3 = static_cast<u8>(c3Int);
178 }
179
180 // Boolean flags
181 if (segJson.contains("sel") && segJson["sel"].is_bool()) {
182 seg.mSel = segJson["sel"] | false;
183 }
184
185 if (segJson.contains("rev") && segJson["rev"].is_bool()) {
186 seg.mRev = segJson["rev"] | false;
187 }
188
189 if (segJson.contains("mi") && segJson["mi"].is_bool()) {
190 seg.mMi = segJson["mi"] | false;
191 }
192
193 if (segJson.contains("o1") && segJson["o1"].is_bool()) {
194 seg.mO1 = segJson["o1"] | false;
195 }
196
197 if (segJson.contains("o2") && segJson["o2"].is_bool()) {
198 seg.mO2 = segJson["o2"] | false;
199 }
200
201 if (segJson.contains("o3") && segJson["o3"].is_bool()) {
202 seg.mO3 = segJson["o3"] | false;
203 }
204
205 if (segJson.contains("si") && segJson["si"].is_int()) {
206 i64 siInt = segJson["si"] | 0;
207 if (siInt < 0) siInt = 0;
208 if (siInt > 3) siInt = 3;
209 seg.mSi = static_cast<u8>(siInt);
210 }
211
212 if (segJson.contains("m12") && segJson["m12"].is_int()) {
213 i64 m12Int = segJson["m12"] | 0;
214 if (m12Int < 0) m12Int = 0;
215 if (m12Int > 3) m12Int = 3;
216 seg.mM12 = static_cast<u8>(m12Int);
217 }
218
219 if (segJson.contains("rpt") && segJson["rpt"].is_bool()) {
220 seg.mRpt = segJson["rpt"] | false;
221 }
222
223 if (segJson.contains("n") && segJson["n"].is_string()) {
224 seg.mName = segJson["n"] | fl::string("");
225 }
226
227 // Parse "col" field (color slots)
228 if (segJson.contains("col") && segJson["col"].is_array()) {
229 seg.mColors.clear();
230 for (size_t i = 0; i < segJson["col"].size(); i++) {
231 const fl::json& colJson = segJson["col"][i];
232
233 if (colJson.is_array()) {
234 // RGB(W) array format: [R,G,B] or [R,G,B,W]
236 for (size_t j = 0; j < colJson.size() && j < 4; j++) {
237 if (colJson[j].is_int()) {
238 i64 val = colJson[j] | 0;
239 if (val < 0) val = 0;
240 if (val > 255) val = 255;
241 color.push_back(static_cast<u8>(val));
242 }
243 }
244 if (color.size() >= 3) {
245 seg.mColors.push_back(color);
246 }
247 } else if (colJson.is_string()) {
248 // Hex string format: "RRGGBB" or "#RRGGBB"
249 fl::string hexStr = colJson | fl::string("");
250 u8 r, g, b;
251 if (parseHexColor(hexStr, r, g, b)) {
253 color.push_back(r);
254 color.push_back(g);
255 color.push_back(b);
256 seg.mColors.push_back(color);
257 } else {
258 FL_WARN("WLED: invalid hex color string: " << hexStr);
259 }
260 }
261 }
262 }
263
264 // Parse "i" field (individual LED control)
265 if (segJson.contains("i") && segJson["i"].is_array()) {
266 seg.mIndividualLeds.clear();
267
268 size_t ledIndex = 0;
269 for (size_t i = 0; i < segJson["i"].size(); i++) {
270 const fl::json& ledJson = segJson["i"][i];
271
272 if (!ledJson.is_string()) {
273 continue;
274 }
275
276 fl::string ledStr = ledJson | fl::string("");
277
278 // Check for indexed or range format: "RRGGBB|index" or "RRGGBB|start-end"
279 size_t pipePos = ledStr.find('|');
280
281 fl::string hexStr;
282 size_t startIdx = ledIndex;
283 size_t endIdx = ledIndex;
284
285 if (pipePos != fl::string::npos) {
286 // Has index/range specifier
287 hexStr = ledStr.substr(0, pipePos);
288 fl::string indexStr = ledStr.substr(pipePos + 1);
289
290 // Check for range: "start-end"
291 size_t dashPos = indexStr.find('-');
292 if (dashPos != fl::string::npos) {
293 // Range format
294 fl::string startStr = indexStr.substr(0, dashPos);
295 fl::string endStr = indexStr.substr(dashPos + 1);
296
297 // Parse start and end indices
298 char* endptr;
299 long startVal = fl::strtol(startStr.c_str(), &endptr, 10);
300 if (endptr == startStr.c_str() || startVal < 0) {
301 FL_WARN("WLED: invalid range start index: " << startStr);
302 continue;
303 }
304
305 long endVal = fl::strtol(endStr.c_str(), &endptr, 10);
306 if (endptr == endStr.c_str() || endVal < 0) {
307 FL_WARN("WLED: invalid range end index: " << endStr);
308 continue;
309 }
310
311 startIdx = static_cast<size_t>(startVal);
312 endIdx = static_cast<size_t>(endVal);
313 } else {
314 // Single index format
315 char* endptr;
316 long idxVal = fl::strtol(indexStr.c_str(), &endptr, 10);
317 if (endptr == indexStr.c_str() || idxVal < 0) {
318 FL_WARN("WLED: invalid LED index: " << indexStr);
319 continue;
320 }
321 startIdx = endIdx = static_cast<size_t>(idxVal);
322 }
323 } else {
324 // Sequential format (no index specifier)
325 hexStr = ledStr;
326 ledIndex++;
327 }
328
329 // Parse hex color
330 u8 r, g, b;
331 if (!parseHexColor(hexStr, r, g, b)) {
332 FL_WARN("WLED: invalid hex color in individual LED: " << hexStr);
333 continue;
334 }
335
336 // Ensure array is large enough
337 size_t maxIdx = (endIdx > startIdx) ? endIdx : startIdx;
338 if (seg.mIndividualLeds.size() <= maxIdx) {
339 seg.mIndividualLeds.resize(maxIdx + 1);
340 }
341
342 // Set LEDs in range
343 for (size_t idx = startIdx; idx <= endIdx; idx++) {
344 seg.mIndividualLeds[idx].clear();
345 seg.mIndividualLeds[idx].push_back(r);
346 seg.mIndividualLeds[idx].push_back(g);
347 seg.mIndividualLeds[idx].push_back(b);
348 }
349 }
350 }
351}
352
353} // namespace wled
354} // namespace fl
fl::size find(const char &value) const FL_NO_EXCEPT
const char * c_str() const FL_NO_EXCEPT
size_t size() const FL_NO_EXCEPT
Definition json.h:798
bool is_array() const FL_NO_EXCEPT
Definition json.h:389
bool is_string() const FL_NO_EXCEPT
Definition json.h:388
string substr(fl::size start, fl::size length) const FL_NO_EXCEPT
static constexpr fl::size npos
Definition string.h:204
#define FL_WARN(...)
Definition log.h:437
Centralized logging categories for FastLED hardware interfaces and subsystems.
bool parseHexColor(const fl::string &hexStr, u8 &r, u8 &g, u8 &b) FL_NO_EXCEPT
Parse hex color string to RGB components.
fl::string rgbToHex(u8 r, u8 g, u8 b) FL_NO_EXCEPT
Convert RGB components to hex string.
void parseSegmentFields(const fl::json &segJson, WLEDSegment &seg) FL_NO_EXCEPT
Parse all fields from a segment JSON object into a WLEDSegment.
unsigned char u8
Definition stdint.h:131
u8 u8 fract8 blur_amount FL_NO_EXCEPT
Definition blur.h:186
const hex_t hex
Definition ios.cpp.hpp:11
expected< T, E > result
Alias for expected (Rust-style naming)
Definition result.h:31
InputGamut g
Definition rgbw.h:125
FASTLED_FORCE_INLINE long strtol(const char *str, char **endptr, int base) FL_NO_EXCEPT
Definition cstdlib.h:61
Base definition for an LED controller.
Definition crgb.hpp:179
WLED segment configuration.
Definition segment.h:15
fl::i64 i64
Definition stdint.h:221