@@ -43,6 +43,19 @@ type Air struct {
43
43
Pressure * float64
44
44
CO2 * float64
45
45
LightIntensity * float64
46
+ Location * string
47
+ }
48
+
49
+ // Motion is a motion measurement.
50
+ type Motion struct {
51
+ Detected * bool
52
+ Count * float64
53
+ }
54
+
55
+ // Action is an action measurement.
56
+ type Action struct {
57
+ Motion Motion
58
+ ContactState * string
46
59
}
47
60
48
61
// Wind is a wind measurement.
@@ -51,12 +64,46 @@ type Wind struct {
51
64
Direction * float64
52
65
}
53
66
67
+ // Rain is a rain measurement.
68
+ type Rain struct {
69
+ Intensity * float64
70
+ Cumulative * float64
71
+ }
72
+
73
+ // Water is a water measurement.
74
+ type Water struct {
75
+ Leak * bool
76
+ Temperature * float64
77
+ }
78
+
79
+ // WaterMetering is a water metering measurement.
80
+ type WaterMetering struct {
81
+ Total * float64
82
+ }
83
+
84
+ // Metering is a metering measurement.
85
+ type Metering struct {
86
+ Water WaterMetering
87
+ }
88
+
89
+ // Position is a position measurement.
90
+ type Position struct {
91
+ Latitude * float64
92
+ Longitude * float64
93
+ }
94
+
54
95
// Measurement is a measurement.
55
96
type Measurement struct {
56
- Time * time.Time
57
- Soil Soil
58
- Air Air
59
- Wind Wind
97
+ Time * time.Time
98
+ Battery * float64
99
+ Soil Soil
100
+ Air Air
101
+ Wind Wind
102
+ Rain Rain
103
+ Water Water
104
+ Metering Metering
105
+ Action Action
106
+ Position Position
60
107
}
61
108
62
109
var (
@@ -65,6 +112,10 @@ var (
65
112
"field_minimum" ,
66
113
"`{path}` should be equal or greater than `{minimum}`" ,
67
114
)
115
+ errFieldNotAllowed = errors .DefineDataLoss (
116
+ "field_not_allowed" ,
117
+ "`{path}` should be one of `{allowed}`" ,
118
+ )
68
119
//nolint:unused
69
120
errFieldExclusiveMinimum = errors .DefineDataLoss (
70
121
"field_exclusive_minimum" ,
@@ -141,6 +192,29 @@ func parseNumber(selector func(dst *Measurement) **float64, vals ...fieldValidat
141
192
}
142
193
}
143
194
195
+ // parseString parses and validates a string.
196
+ func parseString (selector func (dst * Measurement ) * * string , allowed []string ) fieldParser {
197
+ return func (dst * Measurement , src * structpb.Value , path string ) []error {
198
+ val , ok := src .Kind .(* structpb.Value_StringValue )
199
+ if ! ok {
200
+ return []error {errFieldType .WithAttributes ("path" , path )}
201
+ }
202
+ p := val .StringValue
203
+ for _ , e := range allowed {
204
+ if p == e {
205
+ * selector (dst ) = & p
206
+ return nil
207
+ }
208
+ }
209
+ return []error {
210
+ errFieldNotAllowed .WithAttributes (
211
+ "path" , path ,
212
+ "allowed" , allowed ,
213
+ ),
214
+ }
215
+ }
216
+ }
217
+
144
218
// parsePercentage parses and validates a percentage.
145
219
func parsePercentage (selector func (dst * Measurement ) * * float64 ) fieldParser {
146
220
return parseNumber (
@@ -159,6 +233,18 @@ func parseConcentration(selector func(dst *Measurement) **float64) fieldParser {
159
233
)
160
234
}
161
235
236
+ // parseBoolean parses a boolean.
237
+ func parseBoolean (selector func (dst * Measurement ) * * bool ) fieldParser {
238
+ return func (dst * Measurement , src * structpb.Value , path string ) []error {
239
+ val , ok := src .Kind .(* structpb.Value_BoolValue )
240
+ if ! ok {
241
+ return []error {errFieldType .WithAttributes ("path" , path )}
242
+ }
243
+ * selector (dst ) = & val .BoolValue
244
+ return nil
245
+ }
246
+ }
247
+
162
248
// minimum returns a field validator that checks the inclusive minimum.
163
249
func minimum [T constraints.Ordered ](min T ) fieldValidator [T ] {
164
250
return func (v T , path string ) error {
@@ -214,6 +300,12 @@ func exclusiveMaximum[T constraints.Ordered](max T) fieldValidator[T] {
214
300
}
215
301
216
302
var fieldParsers = map [string ]fieldParser {
303
+ "battery" : parseNumber (
304
+ func (dst * Measurement ) * * float64 {
305
+ return & dst .Battery
306
+ },
307
+ minimum (0.0 ),
308
+ ),
217
309
"time" : parseTime (
218
310
func (dst * Measurement ) * * time.Time {
219
311
return & dst .Time
@@ -304,6 +396,15 @@ var fieldParsers = map[string]fieldParser{
304
396
},
305
397
minimum (0.0 ),
306
398
),
399
+ "air.location" : parseString (
400
+ func (dst * Measurement ) * * string {
401
+ return & dst .Air .Location
402
+ },
403
+ []string {
404
+ "indoor" ,
405
+ "outdoor" ,
406
+ },
407
+ ),
307
408
"wind" : object (
308
409
func (dst * Measurement ) * Wind {
309
410
return & dst .Wind
@@ -322,6 +423,103 @@ var fieldParsers = map[string]fieldParser{
322
423
minimum (0.0 ),
323
424
exclusiveMaximum (360.0 ),
324
425
),
426
+ "rain" : object (
427
+ func (dst * Measurement ) * Rain {
428
+ return & dst .Rain
429
+ },
430
+ ),
431
+ "rain.intensity" : parseNumber (
432
+ func (dst * Measurement ) * * float64 {
433
+ return & dst .Rain .Intensity
434
+ },
435
+ minimum (0.0 ),
436
+ ),
437
+ "rain.cumulative" : parseNumber (
438
+ func (dst * Measurement ) * * float64 {
439
+ return & dst .Rain .Cumulative
440
+ },
441
+ minimum (0.0 ),
442
+ ),
443
+ "water" : object (
444
+ func (dst * Measurement ) * Water {
445
+ return & dst .Water
446
+ },
447
+ ),
448
+ "water.leak" : parseBoolean (
449
+ func (dst * Measurement ) * * bool {
450
+ return & dst .Water .Leak
451
+ },
452
+ ),
453
+ "water.temperature" : parseNumber (
454
+ func (dst * Measurement ) * * float64 {
455
+ return & dst .Water .Temperature
456
+ },
457
+ ),
458
+ "metering" : object (
459
+ func (dst * Measurement ) * Metering {
460
+ return & dst .Metering
461
+ },
462
+ ),
463
+ "metering.water" : object (
464
+ func (dst * Measurement ) * WaterMetering {
465
+ return & dst .Metering .Water
466
+ },
467
+ ),
468
+ "metering.water.total" : parseNumber (
469
+ func (dst * Measurement ) * * float64 {
470
+ return & dst .Metering .Water .Total
471
+ },
472
+ minimum (0.0 ),
473
+ ),
474
+ "action" : object (
475
+ func (dst * Measurement ) * Action {
476
+ return & dst .Action
477
+ },
478
+ ),
479
+ "action.motion" : object (
480
+ func (dst * Measurement ) * Motion {
481
+ return & dst .Action .Motion
482
+ },
483
+ ),
484
+ "action.motion.detected" : parseBoolean (
485
+ func (dst * Measurement ) * * bool {
486
+ return & dst .Action .Motion .Detected
487
+ },
488
+ ),
489
+ "action.motion.count" : parseNumber (
490
+ func (dst * Measurement ) * * float64 {
491
+ return & dst .Action .Motion .Count
492
+ },
493
+ minimum (0.0 ),
494
+ ),
495
+ "action.contactState" : parseString (
496
+ func (dst * Measurement ) * * string {
497
+ return & dst .Action .ContactState
498
+ },
499
+ []string {
500
+ "open" ,
501
+ "closed" ,
502
+ },
503
+ ),
504
+ "position" : object (
505
+ func (dst * Measurement ) * Position {
506
+ return & dst .Position
507
+ },
508
+ ),
509
+ "position.latitude" : parseNumber (
510
+ func (dst * Measurement ) * * float64 {
511
+ return & dst .Position .Latitude
512
+ },
513
+ minimum (- 90.0 ),
514
+ maximum (90.0 ),
515
+ ),
516
+ "position.longitude" : parseNumber (
517
+ func (dst * Measurement ) * * float64 {
518
+ return & dst .Position .Longitude
519
+ },
520
+ minimum (- 180.0 ),
521
+ maximum (180.0 ),
522
+ ),
325
523
}
326
524
327
525
// ParsedMeasurement is the result of parsing measurements with Parse.
0 commit comments