@alteriom/painlessmesh 1.7.6 → 1.7.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +177 -0
- package/README.md +96 -14
- package/RELEASE_GUIDE.md +36 -0
- package/docs/API_DESIGN_GUIDELINES.md +414 -0
- package/docs/BOOLEAN_NAMING_CONVENTION.md +235 -0
- package/docs/MQTT_BRIDGE_COMMANDS.md +10 -10
- package/docs/MQTT_BRIDGE_IMPLEMENTATION_SUMMARY.md +1 -1
- package/docs/MQTT_SCHEMA_COMPLIANCE.md +57 -2
- package/docs/PHASE1_GUIDE.md +1 -1
- package/docs/alteriom/overview.md +25 -2
- package/docs/architecture/plugin-system.md +1 -1
- package/docs/archive/RELEASE_SUMMARY.md +1 -1
- package/docs/releases/RELEASE_CHECKLIST_v1.7.6.md +389 -0
- package/docs/releases/RELEASE_SUMMARY_v1.7.7.md +391 -0
- package/docs/v1.7.7_MQTT_IMPROVEMENTS.md +794 -0
- package/docs/wiki/API-Reference.md +2 -2
- package/docs/wiki/Complete-Documentation.md +1 -1
- package/examples/alteriom/README.md +150 -4
- package/examples/alteriom/alteriom.ino +1 -1
- package/examples/alteriom/alteriom_sensor_package.hpp +914 -4
- package/examples/alteriomImproved/alteriom_sensor_package.hpp +1 -1
- package/examples/alteriomImproved/improved_sensor_node.ino +1 -1
- package/examples/alteriomMetricsHealth/alteriom_sensor_package.hpp +796 -0
- package/examples/alteriomMetricsHealth/metrics_health_node.ino +418 -0
- package/examples/alteriomMetricsHealth/platformio.ini +26 -0
- package/examples/alteriomPhase1/alteriom_sensor_package.hpp +1 -1
- package/examples/alteriomPhase1/phase1_features.ino +2 -2
- package/examples/alteriomPhase2/alteriom_sensor_package.hpp +1 -1
- package/examples/alteriomSensorNode/alteriom_sensor_node.ino +1 -1
- package/examples/alteriomSensorNode/alteriom_sensor_package.hpp +1 -1
- package/examples/bridge/enhanced_mqtt_bridge.hpp +610 -0
- package/examples/bridge/enhanced_mqtt_bridge_example.ino +226 -0
- package/examples/meshCommandNode/alteriom_sensor_package.hpp +1 -1
- package/examples/mqttCommandBridge/alteriom_sensor_package.hpp +1 -1
- package/examples/mqttTopologyTest/mqttTopologyTest.ino +5 -1
- package/library.json +1 -1
- package/library.properties +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
# API Design Guidelines for Alteriom Packages
|
|
2
|
+
|
|
3
|
+
This document provides guidelines for designing consistent and maintainable JSON configuration structures in Alteriom packages, particularly for StatusPackage and related message types.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Overview](#overview)
|
|
8
|
+
- [Nesting vs Flat Structure Guidelines](#nesting-vs-flat-structure-guidelines)
|
|
9
|
+
- [Current Structure Patterns](#current-structure-patterns)
|
|
10
|
+
- [Decision Tree](#decision-tree)
|
|
11
|
+
- [Examples](#examples)
|
|
12
|
+
- [Best Practices](#best-practices)
|
|
13
|
+
|
|
14
|
+
## Overview
|
|
15
|
+
|
|
16
|
+
Alteriom packages use JSON serialization for configuration and status data. This document establishes clear patterns for when to use nested structures versus flat key-value pairs to ensure consistency and maintainability across the codebase.
|
|
17
|
+
|
|
18
|
+
**Related Issues:**
|
|
19
|
+
- [Issue #28](https://github.com/Alteriom/painlessMesh/issues/28) - Inconsistent Nested vs Flat Configuration Structure
|
|
20
|
+
- [Issue #29](https://github.com/Alteriom/painlessMesh/issues/29) - Inconsistent Optional vs Required Field Serialization Pattern
|
|
21
|
+
- [PR #36](https://github.com/Alteriom/painlessMesh/pull/36) - Documented nesting patterns (this file)
|
|
22
|
+
- [PR #37](https://github.com/Alteriom/painlessMesh/pull/37) - Removed conditional serialization for predictable JSON structure
|
|
23
|
+
|
|
24
|
+
### Key Principles
|
|
25
|
+
|
|
26
|
+
1. **Consistency over perfection** - Follow existing patterns in similar sections
|
|
27
|
+
2. **Simplicity by default** - Use flat structures unless nesting provides clear benefits
|
|
28
|
+
3. **Future-proof** - Consider extensibility when designing structures
|
|
29
|
+
4. **Clarity** - Structure should reflect logical grouping
|
|
30
|
+
5. **Predictable structure** - All sections always serialize with default values (addressed in PR #37)
|
|
31
|
+
|
|
32
|
+
## Nesting vs Flat Structure Guidelines
|
|
33
|
+
|
|
34
|
+
### Use FLAT Structure When:
|
|
35
|
+
|
|
36
|
+
- **< 4 total fields** in a configuration section
|
|
37
|
+
- **No clear logical subsystems** within the section
|
|
38
|
+
- **Simple value types** without complex relationships
|
|
39
|
+
- **Low likelihood of expansion** in the future
|
|
40
|
+
|
|
41
|
+
**Benefits:**
|
|
42
|
+
- Simpler code (fewer nested object creations)
|
|
43
|
+
- Easier to parse and validate
|
|
44
|
+
- More concise JSON output
|
|
45
|
+
- Faster serialization/deserialization
|
|
46
|
+
|
|
47
|
+
### Use NESTED Structure When:
|
|
48
|
+
|
|
49
|
+
- **3+ fields belong to same logical subsystem**
|
|
50
|
+
- **Clear semantic grouping** exists
|
|
51
|
+
- **Future extensibility anticipated** for subsystem
|
|
52
|
+
- **Subsystem has distinct meaning** separate from parent
|
|
53
|
+
|
|
54
|
+
**Benefits:**
|
|
55
|
+
- Better logical organization
|
|
56
|
+
- Easier to add related fields without cluttering parent
|
|
57
|
+
- Clear separation of concerns
|
|
58
|
+
- More extensible architecture
|
|
59
|
+
|
|
60
|
+
## Current Structure Patterns
|
|
61
|
+
|
|
62
|
+
**Important Note (as of PR #37):** All configuration sections now **always serialize** regardless of whether values are at their defaults. This provides predictable JSON structure and eliminates the need for consumers to check key existence. Default values (0, false, "") clearly indicate "not configured" state.
|
|
63
|
+
|
|
64
|
+
### Flat Sections (No Nesting)
|
|
65
|
+
|
|
66
|
+
These sections use simple key-value pairs at a single level:
|
|
67
|
+
|
|
68
|
+
#### Display Configuration
|
|
69
|
+
```json
|
|
70
|
+
"display_config": {
|
|
71
|
+
"enabled": true,
|
|
72
|
+
"brightness": 128,
|
|
73
|
+
"timeout_ms": 30000,
|
|
74
|
+
"timeout_s": 30
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Rationale:** Only 3-4 fields, all directly related to display, no subsystems.
|
|
79
|
+
|
|
80
|
+
#### Power Configuration
|
|
81
|
+
```json
|
|
82
|
+
"power_config": {
|
|
83
|
+
"deep_sleep_enabled": false,
|
|
84
|
+
"deep_sleep_interval_ms": 300000,
|
|
85
|
+
"deep_sleep_interval_s": 300,
|
|
86
|
+
"battery_percent": 85
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Rationale:** Small number of fields (4), even though battery and sleep are different concerns, nesting would add unnecessary complexity.
|
|
91
|
+
|
|
92
|
+
#### MQTT Retry Configuration
|
|
93
|
+
```json
|
|
94
|
+
"mqtt_retry": {
|
|
95
|
+
"max_attempts": 5,
|
|
96
|
+
"circuit_breaker_ms": 60000,
|
|
97
|
+
"circuit_breaker_s": 60,
|
|
98
|
+
"hourly_retry_enabled": true,
|
|
99
|
+
"initial_retry_ms": 1000,
|
|
100
|
+
"initial_retry_s": 1,
|
|
101
|
+
"max_retry_ms": 30000,
|
|
102
|
+
"max_retry_s": 30,
|
|
103
|
+
"backoff_multiplier": 2.0
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
**Rationale:** While this has 9 fields with distinct concerns (retry policy vs backoff strategy), it remains flat for simplicity. The retry configuration is cohesive enough that nesting would fragment it without clear benefit.
|
|
108
|
+
|
|
109
|
+
### Nested Sections (With Subsystems)
|
|
110
|
+
|
|
111
|
+
These sections use nested objects for logical grouping:
|
|
112
|
+
|
|
113
|
+
#### Sensor Configuration with Calibration
|
|
114
|
+
```json
|
|
115
|
+
"sensors": {
|
|
116
|
+
"read_interval_ms": 30000,
|
|
117
|
+
"read_interval_s": 30,
|
|
118
|
+
"transmission_interval_ms": 60000,
|
|
119
|
+
"transmission_interval_s": 60,
|
|
120
|
+
"calibration": {
|
|
121
|
+
"temperature_offset": 0.5,
|
|
122
|
+
"humidity_offset": -2.0,
|
|
123
|
+
"pressure_offset": 0.0
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Rationale:** Calibration is a distinct subsystem with its own semantic meaning. It's optional, extensible, and conceptually separate from sensor timing configuration.
|
|
129
|
+
|
|
130
|
+
**Benefits of nesting here:**
|
|
131
|
+
- Calibration can be added/removed as a unit
|
|
132
|
+
- Easy to add more calibration fields without cluttering main sensors object
|
|
133
|
+
- Clear semantic boundary - calibration is a specific tuning operation
|
|
134
|
+
|
|
135
|
+
#### Organization Metadata
|
|
136
|
+
```json
|
|
137
|
+
"organization": {
|
|
138
|
+
"organizationId": "org-123",
|
|
139
|
+
"customerId": "cust-456",
|
|
140
|
+
"deviceGroup": "sensors",
|
|
141
|
+
"device_name": "sensor-01",
|
|
142
|
+
"device_location": "warehouse-a",
|
|
143
|
+
"device_secret_set": true
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**Rationale:** Organization metadata is an optional, self-contained subsystem that may not be present on all devices.
|
|
148
|
+
|
|
149
|
+
## Decision Tree
|
|
150
|
+
|
|
151
|
+
Use this decision tree when designing new configuration sections:
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
START: New configuration section needed
|
|
155
|
+
│
|
|
156
|
+
├─ Does section have < 4 fields?
|
|
157
|
+
│ ├─ YES → Use FLAT structure
|
|
158
|
+
│ └─ NO → Continue
|
|
159
|
+
│
|
|
160
|
+
├─ Do 3+ fields belong to same logical subsystem?
|
|
161
|
+
│ ├─ NO → Use FLAT structure
|
|
162
|
+
│ └─ YES → Continue
|
|
163
|
+
│
|
|
164
|
+
├─ Is subsystem likely to grow in future?
|
|
165
|
+
│ ├─ NO → Consider FLAT (unless strong semantic grouping)
|
|
166
|
+
│ └─ YES → Continue
|
|
167
|
+
│
|
|
168
|
+
├─ Would nesting improve clarity significantly?
|
|
169
|
+
│ ├─ NO → Use FLAT structure
|
|
170
|
+
│ └─ YES → Use NESTED structure
|
|
171
|
+
│
|
|
172
|
+
END
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Examples
|
|
176
|
+
|
|
177
|
+
### Example 1: Adding OTA Configuration (Flat Approach)
|
|
178
|
+
|
|
179
|
+
**Scenario:** Adding Over-The-Air update configuration with 3 fields.
|
|
180
|
+
|
|
181
|
+
```cpp
|
|
182
|
+
// C++ Fields
|
|
183
|
+
bool otaEnabled = false;
|
|
184
|
+
TSTRING otaServer = "";
|
|
185
|
+
uint16_t otaPort = 0;
|
|
186
|
+
|
|
187
|
+
// JSON Serialization (FLAT)
|
|
188
|
+
JsonObject ota = jsonObj["ota"].to<JsonObject>();
|
|
189
|
+
ota["enabled"] = otaEnabled;
|
|
190
|
+
ota["server"] = otaServer;
|
|
191
|
+
ota["port"] = otaPort;
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
**Result:**
|
|
195
|
+
```json
|
|
196
|
+
"ota": {
|
|
197
|
+
"enabled": true,
|
|
198
|
+
"server": "ota.example.com",
|
|
199
|
+
"port": 8080
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Decision:** Keep FLAT - only 3 fields, no subsystems.
|
|
204
|
+
|
|
205
|
+
### Example 2: Adding Sensor Thresholds (Nested Approach)
|
|
206
|
+
|
|
207
|
+
**Scenario:** Adding temperature, humidity, and pressure thresholds to sensor configuration.
|
|
208
|
+
|
|
209
|
+
```cpp
|
|
210
|
+
// C++ Fields (added to existing sensor config)
|
|
211
|
+
double tempThresholdMin = -40.0;
|
|
212
|
+
double tempThresholdMax = 85.0;
|
|
213
|
+
double humidityThresholdMin = 0.0;
|
|
214
|
+
double humidityThresholdMax = 100.0;
|
|
215
|
+
|
|
216
|
+
// JSON Serialization (NESTED under sensors)
|
|
217
|
+
JsonObject sensors = jsonObj["sensors"].to<JsonObject>();
|
|
218
|
+
// ... existing sensor fields ...
|
|
219
|
+
|
|
220
|
+
JsonObject thresholds = sensors["thresholds"].to<JsonObject>();
|
|
221
|
+
thresholds["temperature_min"] = tempThresholdMin;
|
|
222
|
+
thresholds["temperature_max"] = tempThresholdMax;
|
|
223
|
+
thresholds["humidity_min"] = humidityThresholdMin;
|
|
224
|
+
thresholds["humidity_max"] = humidityThresholdMax;
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
**Result:**
|
|
228
|
+
```json
|
|
229
|
+
"sensors": {
|
|
230
|
+
"read_interval_ms": 30000,
|
|
231
|
+
"calibration": { ... },
|
|
232
|
+
"thresholds": {
|
|
233
|
+
"temperature_min": -40.0,
|
|
234
|
+
"temperature_max": 85.0,
|
|
235
|
+
"humidity_min": 0.0,
|
|
236
|
+
"humidity_max": 100.0
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
**Decision:** Use NESTED - 4+ fields, clear subsystem (alerting/validation logic), logical grouping.
|
|
242
|
+
|
|
243
|
+
### Example 3: Adding Encoding Configuration (Flat Approach)
|
|
244
|
+
|
|
245
|
+
**Scenario:** Adding message encoding/compression settings.
|
|
246
|
+
|
|
247
|
+
```cpp
|
|
248
|
+
// C++ Fields
|
|
249
|
+
bool compressionEnabled = false;
|
|
250
|
+
TSTRING encodingType = "json";
|
|
251
|
+
uint8_t compressionLevel = 6;
|
|
252
|
+
|
|
253
|
+
// JSON Serialization (FLAT)
|
|
254
|
+
JsonObject encoding = jsonObj["encoding"].to<JsonObject>();
|
|
255
|
+
encoding["compression_enabled"] = compressionEnabled;
|
|
256
|
+
encoding["encoding_type"] = encodingType;
|
|
257
|
+
encoding["compression_level"] = compressionLevel;
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
**Result:**
|
|
261
|
+
```json
|
|
262
|
+
"encoding": {
|
|
263
|
+
"compression_enabled": false,
|
|
264
|
+
"encoding_type": "json",
|
|
265
|
+
"compression_level": 6
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
**Decision:** Keep FLAT - only 3 fields, cohesive purpose.
|
|
270
|
+
|
|
271
|
+
## Best Practices
|
|
272
|
+
|
|
273
|
+
### 1. Be Conservative with Nesting
|
|
274
|
+
|
|
275
|
+
**Rationale:** Flat structures are simpler to implement and consume.
|
|
276
|
+
|
|
277
|
+
**Rule:** When in doubt, start flat. Nesting can be added later if needed, but removing nesting is a breaking change.
|
|
278
|
+
|
|
279
|
+
### 2. Consider Backward Compatibility
|
|
280
|
+
|
|
281
|
+
When adding fields to existing sections:
|
|
282
|
+
|
|
283
|
+
```cpp
|
|
284
|
+
// Deserialization with backward compatibility
|
|
285
|
+
displayTimeout = displayConfig["timeout_ms"] | displayConfig["timeout"] | 0;
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
This allows reading old format (`timeout`) while preferring new format (`timeout_ms`).
|
|
289
|
+
|
|
290
|
+
### 3. Group Optional Subsystems
|
|
291
|
+
|
|
292
|
+
**Good:**
|
|
293
|
+
```json
|
|
294
|
+
"sensors": {
|
|
295
|
+
"read_interval_ms": 30000,
|
|
296
|
+
"calibration": { ... } // Optional, can be omitted entirely
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
**Avoid:**
|
|
301
|
+
```json
|
|
302
|
+
"sensors": {
|
|
303
|
+
"read_interval_ms": 30000,
|
|
304
|
+
"temperature_offset": 0.0, // Mixed levels - unclear if calibration is a concept
|
|
305
|
+
"humidity_offset": 0.0,
|
|
306
|
+
"pressure_offset": 0.0
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### 4. Maintain Consistent Field Naming
|
|
311
|
+
|
|
312
|
+
Follow existing conventions:
|
|
313
|
+
- Time fields: Follow [Time Field Naming Convention](../examples/alteriom/alteriom_sensor_package.hpp#L10-L55)
|
|
314
|
+
- Boolean fields: Follow [Boolean Naming Convention](BOOLEAN_NAMING_CONVENTION.md)
|
|
315
|
+
- Use snake_case for JSON keys
|
|
316
|
+
- Use camelCase for C++ field names
|
|
317
|
+
|
|
318
|
+
### 5. Document Structure Decisions
|
|
319
|
+
|
|
320
|
+
Add comments explaining nesting choices:
|
|
321
|
+
|
|
322
|
+
```cpp
|
|
323
|
+
// Display configuration (flat - only 3 fields, no subsystems)
|
|
324
|
+
JsonObject displayConfig = jsonObj["display_config"].to<JsonObject>();
|
|
325
|
+
|
|
326
|
+
// Sensor configuration with nested calibration (calibration is distinct subsystem)
|
|
327
|
+
JsonObject sensors = jsonObj["sensors"].to<JsonObject>();
|
|
328
|
+
JsonObject calibration = sensors["calibration"].to<JsonObject>();
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### 6. Test Structure Consistency
|
|
332
|
+
|
|
333
|
+
Create tests to validate structure patterns:
|
|
334
|
+
|
|
335
|
+
```cpp
|
|
336
|
+
TEST(StatusPackage, StructureConsistency) {
|
|
337
|
+
alteriom::StatusPackage pkg;
|
|
338
|
+
pkg.tempOffset = 0.5;
|
|
339
|
+
|
|
340
|
+
JsonDocument doc;
|
|
341
|
+
JsonObject obj = doc.to<JsonObject>();
|
|
342
|
+
pkg.addTo(std::move(obj));
|
|
343
|
+
|
|
344
|
+
// Verify nested structures
|
|
345
|
+
REQUIRE(obj["sensors"]["calibration"].is<JsonObject>());
|
|
346
|
+
|
|
347
|
+
// Verify flat structures remain flat
|
|
348
|
+
REQUIRE(obj["display_config"]["enabled"].is<bool>());
|
|
349
|
+
REQUIRE_FALSE(obj["display_config"].containsKey("nested_section"));
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
## Migration Path
|
|
354
|
+
|
|
355
|
+
If restructuring becomes necessary:
|
|
356
|
+
|
|
357
|
+
1. **Add new structure** while maintaining old structure
|
|
358
|
+
2. **Support both formats** in deserialization
|
|
359
|
+
3. **Deprecate old format** (document in release notes)
|
|
360
|
+
4. **Remove old format** in next major version
|
|
361
|
+
|
|
362
|
+
```cpp
|
|
363
|
+
// Example: Supporting both flat and nested
|
|
364
|
+
if (jsonObj["network"]["wifi"].is<JsonObject>()) {
|
|
365
|
+
// New nested format
|
|
366
|
+
JsonObject wifi = jsonObj["network"]["wifi"];
|
|
367
|
+
wifiSSID = wifi["ssid"].as<TSTRING>();
|
|
368
|
+
} else {
|
|
369
|
+
// Old flat format (deprecated)
|
|
370
|
+
wifiSSID = jsonObj["network"]["wifi_ssid"].as<TSTRING>();
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
## Summary
|
|
375
|
+
|
|
376
|
+
| Criteria | Flat Structure | Nested Structure |
|
|
377
|
+
|----------|---------------|------------------|
|
|
378
|
+
| **Field Count** | < 4 fields | 3+ fields in subsystem |
|
|
379
|
+
| **Logical Grouping** | No clear subsystems | Clear semantic grouping |
|
|
380
|
+
| **Extensibility** | Low likelihood of growth | Anticipated expansion |
|
|
381
|
+
| **Complexity** | Simple values | Complex relationships |
|
|
382
|
+
| **Serialization** | Always present (PR #37) | Always present (PR #37) |
|
|
383
|
+
| **Examples** | display_config, power_config, ota, encoding, mqtt_retry | sensors.calibration, organization |
|
|
384
|
+
|
|
385
|
+
**Golden Rule:** When uncertain, prefer flat structures. Nesting should provide clear organizational or extensibility benefits to justify the added complexity.
|
|
386
|
+
|
|
387
|
+
### Resolution of Issue #28
|
|
388
|
+
|
|
389
|
+
Issue #28 identified inconsistent nesting patterns and proposed three options:
|
|
390
|
+
|
|
391
|
+
1. **Option 1: Keep Current Structure (Document Pattern)** ✅ **ADOPTED**
|
|
392
|
+
2. Option 2: Nest Network Configuration (Breaking Change)
|
|
393
|
+
3. Option 3: Nest MQTT Retry Backoff Settings (Minimal Change)
|
|
394
|
+
|
|
395
|
+
**Decision Rationale:**
|
|
396
|
+
- Current flat structure for most sections (display_config, power_config, mqtt_retry) is functional and simple
|
|
397
|
+
- Only sensors.calibration uses nesting, which is justified by its semantic separation
|
|
398
|
+
- Avoiding breaking changes preserves compatibility with existing consumers
|
|
399
|
+
- Clear documentation (this file) addresses the inconsistency concern
|
|
400
|
+
- PR #37's unconditional serialization provides the predictable structure that was the real underlying concern
|
|
401
|
+
|
|
402
|
+
**Result:** The pattern is now documented and validated. Future additions should follow the decision tree in this document.
|
|
403
|
+
|
|
404
|
+
## References
|
|
405
|
+
|
|
406
|
+
- [StatusPackage Implementation](../examples/alteriom/alteriom_sensor_package.hpp)
|
|
407
|
+
- [Boolean Naming Convention](BOOLEAN_NAMING_CONVENTION.md)
|
|
408
|
+
- [Time Field Naming Convention](../examples/alteriom/alteriom_sensor_package.hpp#L10-L55)
|
|
409
|
+
- [Test Cases](../test/catch/catch_alteriom_packages.cpp)
|
|
410
|
+
|
|
411
|
+
## Revision History
|
|
412
|
+
|
|
413
|
+
- **2025-11-04 (PR #37)**: Updated to reflect unconditional serialization pattern - all sections always serialize with default values for predictable structure
|
|
414
|
+
- **2025-11-04 (PR #36)**: Initial version documenting StatusPackage nesting patterns in response to Issue #28
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# Boolean Field Naming Convention
|
|
2
|
+
|
|
3
|
+
**Related Issue**: #27
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This document establishes the standard naming conventions for boolean fields in Alteriom packages, particularly in `StatusPackage`. Consistent naming patterns make the code self-documenting and help developers understand field semantics at a glance.
|
|
8
|
+
|
|
9
|
+
## Three Naming Patterns
|
|
10
|
+
|
|
11
|
+
### Pattern 1: `*Set` Suffix
|
|
12
|
+
|
|
13
|
+
**Purpose**: Indicates that required configuration data has been provided (typically for sensitive data like passwords or secrets).
|
|
14
|
+
|
|
15
|
+
**When to use**:
|
|
16
|
+
- Field represents whether configuration data exists
|
|
17
|
+
- Typically used for passwords, secrets, API keys, server URLs
|
|
18
|
+
- Does NOT indicate if the feature is active or working
|
|
19
|
+
|
|
20
|
+
**Examples**:
|
|
21
|
+
```cpp
|
|
22
|
+
bool deviceSecretSet = false; // Has device secret been configured?
|
|
23
|
+
bool wifiPasswordSet = false; // Has WiFi password been provided?
|
|
24
|
+
bool meshPasswordSet = false; // Has mesh password been provided?
|
|
25
|
+
bool otaServerSet = false; // Has OTA server URL been configured?
|
|
26
|
+
bool mqttBrokerSet = false; // Has MQTT broker been configured?
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Semantic meaning**:
|
|
30
|
+
- `true` = Configuration data has been provided
|
|
31
|
+
- `false` = Configuration data is missing or not yet provided
|
|
32
|
+
- Does NOT indicate the feature is enabled or currently working
|
|
33
|
+
|
|
34
|
+
### Pattern 2: `*Enabled` Suffix
|
|
35
|
+
|
|
36
|
+
**Purpose**: Indicates that a feature is currently active or turned on.
|
|
37
|
+
|
|
38
|
+
**When to use**:
|
|
39
|
+
- Field represents a feature toggle (on/off)
|
|
40
|
+
- User or system can enable/disable the feature
|
|
41
|
+
- Feature state is controllable and intentional
|
|
42
|
+
|
|
43
|
+
**Examples**:
|
|
44
|
+
```cpp
|
|
45
|
+
bool displayEnabled = false; // Is display feature enabled?
|
|
46
|
+
bool deepSleepEnabled = false; // Is deep sleep mode enabled?
|
|
47
|
+
bool mqttHourlyRetryEnabled = false; // Is hourly retry feature enabled?
|
|
48
|
+
bool otaEnabled = false; // Are OTA updates enabled?
|
|
49
|
+
bool encryptionEnabled = false; // Is data encryption enabled?
|
|
50
|
+
bool encodingEnabled = false; // Is data encoding enabled?
|
|
51
|
+
bool logTimestampEnabled = false; // Are log timestamps enabled?
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Semantic meaning**:
|
|
55
|
+
- `true` = Feature is currently active/turned on
|
|
56
|
+
- `false` = Feature is currently inactive/turned off
|
|
57
|
+
- Independent of whether required configuration exists
|
|
58
|
+
|
|
59
|
+
### Pattern 3: Runtime State (`is*` Prefix or `*Connected`)
|
|
60
|
+
|
|
61
|
+
**Purpose**: Indicates current runtime status or operational state (not configuration).
|
|
62
|
+
|
|
63
|
+
**When to use**:
|
|
64
|
+
- Field represents current operational status
|
|
65
|
+
- Status changes at runtime based on system behavior
|
|
66
|
+
- Not directly controlled by configuration
|
|
67
|
+
|
|
68
|
+
**Examples**:
|
|
69
|
+
```cpp
|
|
70
|
+
bool isConfigured = false; // Has device completed configuration?
|
|
71
|
+
bool mqttConnected = false; // Currently connected to MQTT broker?
|
|
72
|
+
bool meshIsRoot = false; // Is this node currently the mesh root?
|
|
73
|
+
bool isOnline = false; // Is device currently online?
|
|
74
|
+
bool wifiConnected = false; // Currently connected to WiFi?
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Semantic meaning**:
|
|
78
|
+
- `true` = Currently in this state
|
|
79
|
+
- `false` = Not currently in this state
|
|
80
|
+
- Reflects actual runtime conditions, not configuration
|
|
81
|
+
|
|
82
|
+
## Combining Patterns
|
|
83
|
+
|
|
84
|
+
A feature may legitimately have multiple boolean fields using different patterns:
|
|
85
|
+
|
|
86
|
+
### Example: OTA (Over-The-Air) Updates
|
|
87
|
+
|
|
88
|
+
```cpp
|
|
89
|
+
bool otaServerSet = false; // Has OTA server URL been configured? (*Set)
|
|
90
|
+
bool otaEnabled = false; // Are OTA updates enabled? (*Enabled)
|
|
91
|
+
bool otaInProgress = false; // Is an OTA update currently running? (is* / runtime state)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Valid states**:
|
|
95
|
+
- `otaServerSet=true, otaEnabled=false` → Server configured but feature disabled
|
|
96
|
+
- `otaServerSet=false, otaEnabled=true` → Feature enabled but no server (invalid/warning state)
|
|
97
|
+
- `otaServerSet=true, otaEnabled=true` → Fully configured and active
|
|
98
|
+
- `otaServerSet=true, otaEnabled=true, otaInProgress=true` → Update in progress
|
|
99
|
+
|
|
100
|
+
### Example: MQTT Connection
|
|
101
|
+
|
|
102
|
+
```cpp
|
|
103
|
+
bool mqttBrokerSet = false; // Has MQTT broker been configured? (*Set)
|
|
104
|
+
bool mqttEnabled = false; // Is MQTT feature enabled? (*Enabled)
|
|
105
|
+
bool mqttConnected = false; // Currently connected to broker? (runtime state)
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Current StatusPackage Implementation
|
|
109
|
+
|
|
110
|
+
### Existing `*Set` Fields (Build 8057)
|
|
111
|
+
```cpp
|
|
112
|
+
bool deviceSecretSet = false; // Whether device secret is configured
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Potential additions** (if needed):
|
|
116
|
+
```cpp
|
|
117
|
+
bool wifiPasswordSet = false; // Whether WiFi password is configured
|
|
118
|
+
bool meshPasswordSet = false; // Whether mesh password is configured
|
|
119
|
+
bool mqttBrokerSet = false; // Whether MQTT broker is configured
|
|
120
|
+
bool otaServerSet = false; // Whether OTA server URL is configured
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Existing `*Enabled` Fields
|
|
124
|
+
```cpp
|
|
125
|
+
bool displayEnabled = false; // Display feature enabled
|
|
126
|
+
bool deepSleepEnabled = false; // Deep sleep feature enabled
|
|
127
|
+
bool mqttHourlyRetryEnabled = false; // Hourly retry feature enabled
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Potential additions** (if needed):
|
|
131
|
+
```cpp
|
|
132
|
+
bool meshEnabled = false; // Is WiFi mesh feature enabled?
|
|
133
|
+
bool otaEnabled = false; // Are OTA updates enabled?
|
|
134
|
+
bool encryptionEnabled = false; // Is encryption enabled?
|
|
135
|
+
bool encodingEnabled = false; // Is data encoding enabled?
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Potential Runtime State Fields
|
|
139
|
+
Currently, StatusPackage does not have explicit runtime state fields. If needed in the future:
|
|
140
|
+
|
|
141
|
+
```cpp
|
|
142
|
+
bool isConfigured = false; // Device has complete valid configuration
|
|
143
|
+
bool mqttConnected = false; // Currently connected to MQTT broker
|
|
144
|
+
bool meshIsRoot = false; // Currently acting as mesh root
|
|
145
|
+
bool wifiConnected = false; // Currently connected to WiFi
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Best Practices
|
|
149
|
+
|
|
150
|
+
### DO ✅
|
|
151
|
+
|
|
152
|
+
1. **Use `*Set` for configuration presence**
|
|
153
|
+
```cpp
|
|
154
|
+
bool deviceSecretSet = false; // ✅ Indicates if secret is configured
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
2. **Use `*Enabled` for feature toggles**
|
|
158
|
+
```cpp
|
|
159
|
+
bool displayEnabled = false; // ✅ Indicates if feature is on/off
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
3. **Use `is*` or `*Connected` for runtime state**
|
|
163
|
+
```cpp
|
|
164
|
+
bool isConfigured = false; // ✅ Indicates current state
|
|
165
|
+
bool mqttConnected = false; // ✅ Indicates connection status
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
4. **Document field purpose clearly**
|
|
169
|
+
```cpp
|
|
170
|
+
bool otaServerSet = false; // Has OTA server URL been configured? (Build XXXX)
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### DON'T ❌
|
|
174
|
+
|
|
175
|
+
1. **Don't mix patterns without clear semantics**
|
|
176
|
+
```cpp
|
|
177
|
+
bool wifiSet = false; // ❌ Ambiguous - set to what? On/off or configured?
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
2. **Don't use generic boolean names**
|
|
181
|
+
```cpp
|
|
182
|
+
bool wifi = false; // ❌ Unclear meaning
|
|
183
|
+
bool display = false; // ❌ What about display?
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
3. **Don't use `*Set` for feature toggles**
|
|
187
|
+
```cpp
|
|
188
|
+
bool displaySet = false; // ❌ Confusing - use displayEnabled instead
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
4. **Don't use `*Enabled` for configuration presence**
|
|
192
|
+
```cpp
|
|
193
|
+
bool deviceSecretEnabled = false; // ❌ Confusing - use deviceSecretSet instead
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Validation
|
|
197
|
+
|
|
198
|
+
When adding new boolean fields, ask these questions:
|
|
199
|
+
|
|
200
|
+
1. **Does this field indicate configuration presence?**
|
|
201
|
+
- YES → Use `*Set` suffix
|
|
202
|
+
- Example: `mqttBrokerSet`, `deviceSecretSet`
|
|
203
|
+
|
|
204
|
+
2. **Does this field toggle a feature on/off?**
|
|
205
|
+
- YES → Use `*Enabled` suffix
|
|
206
|
+
- Example: `displayEnabled`, `otaEnabled`
|
|
207
|
+
|
|
208
|
+
3. **Does this field reflect current runtime state?**
|
|
209
|
+
- YES → Use `is*` prefix or `*Connected` suffix
|
|
210
|
+
- Example: `isConfigured`, `mqttConnected`
|
|
211
|
+
|
|
212
|
+
4. **Does this field fit multiple categories?**
|
|
213
|
+
- Consider creating separate fields for each semantic meaning
|
|
214
|
+
- Example: `otaServerSet` AND `otaEnabled` AND `otaInProgress`
|
|
215
|
+
|
|
216
|
+
## Benefits
|
|
217
|
+
|
|
218
|
+
1. **Self-Documenting Code**: Field names clearly indicate semantic meaning
|
|
219
|
+
2. **Reduced Confusion**: Developers immediately understand what each boolean represents
|
|
220
|
+
3. **Better API Design**: Consistent patterns across entire codebase
|
|
221
|
+
4. **Easier Onboarding**: New developers can infer meaning from field names
|
|
222
|
+
5. **Fewer Bugs**: Clear semantics reduce misunderstandings and implementation errors
|
|
223
|
+
|
|
224
|
+
## References
|
|
225
|
+
|
|
226
|
+
- StatusPackage implementation: `examples/alteriom/alteriom_sensor_package.hpp`
|
|
227
|
+
- Test validation: `test/catch/catch_alteriom_packages.cpp`
|
|
228
|
+
- Time field conventions: See header documentation in `alteriom_sensor_package.hpp`
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
**Document Version**: 1.0
|
|
233
|
+
**Last Updated**: 2025-11-04
|
|
234
|
+
**Status**: Active
|
|
235
|
+
**Applies To**: StatusPackage, EnhancedStatusPackage, and all future Alteriom packages
|