span-panel-api-schema-0 1.1.1__tar.gz → 1.2.0__tar.gz

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.
@@ -40,8 +40,3 @@ coverage_output.log
40
40
  # of which belongs in a repository. The differential that reads them commits its
41
41
  # *verdict* only, never the capture, and skips when the file is absent.
42
42
  tests/fixtures/live_*.json
43
-
44
- # Peer checkouts. CI clones the eBus specification and SpanPanel/panelbench here so
45
- # the provenance checks have something to compare vendored bytes against; the same
46
- # layout works locally if you would rather not point .env at siblings.
47
- /peers/
@@ -9,6 +9,23 @@ rather than by this version number. A release here means this parser changed, ne
9
9
 
10
10
  Pre-releases are not listed separately. A beta is a step towards the next public version, so its changes are folded into that version's entry as they land and are described against the last public release, never against the beta before it.
11
11
 
12
+ ## [1.2.0]
13
+
14
+ The panel's one inverter is reported through `pv_inverters` as well as `pv`. Requires `span-panel-api` **3.6.0 or newer**.
15
+
16
+ ### Added
17
+
18
+ - **`pv_inverters` holds the panel's one inverter** under the key `span-panel-api-schema-1` gives it, so a consumer can read every panel through that field.
19
+ - **`pv.device_id` and `pv.node_id`** give the inverter's flat node id and its `pv_inverters` key.
20
+
21
+ ## [1.1.2]
22
+
23
+ The reference schema document this parser is tested against now ships in the wheel.
24
+
25
+ ### Added
26
+
27
+ - **`span_panel_api_schema_0/reference/homie_schema.json`** — the captured `GET /api/v2/homie/schema` response, shipped as package data so a downstream test suite reads the bytes its pinned version was tested against instead of vendoring a copy.
28
+
12
29
  ## [1.1.1]
13
30
 
14
31
  Still requires `span-panel-api` **3.1.0 or newer**, unchanged.
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: span-panel-api-schema-0
3
- Version: 1.1.1
3
+ Version: 1.2.0
4
4
  Summary: Flat-schema (data-model-version absent) parser for span-panel-api
5
5
  Project-URL: Homepage, https://github.com/SpanPanel/span-panel-api
6
6
  Project-URL: Issues, https://github.com/SpanPanel/span-panel-api/issues
7
7
  Author: SpanPanel
8
8
  License-Expression: MIT
9
9
  Requires-Python: <4.0,>=3.14
10
- Requires-Dist: span-panel-api<4.0,>=3.1.0
10
+ Requires-Dist: span-panel-api<4.0,>=3.6.0
11
11
  Description-Content-Type: text/markdown
12
12
 
13
13
  # span-panel-api-schema-0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "span-panel-api-schema-0"
3
- version = "1.1.1"
3
+ version = "1.2.0"
4
4
  description = "Flat-schema (data-model-version absent) parser for span-panel-api"
5
5
  authors = [
6
6
  {name = "SpanPanel"}
@@ -16,7 +16,10 @@ dependencies = [
16
16
  # for `set_*_target` instead of `set_*_topic`. Against 3.0.x this wheel is
17
17
  # rejected at discovery for carrying names that bootstrap does not require —
18
18
  # so the floor moves with the rename rather than trailing it.
19
- "span-panel-api>=3.1.0,<4.0",
19
+ # Raised to 3.6.0, the first bootstrap whose snapshot carries `pv_inverters`
20
+ # and the inverter's `device_id` and `node_id`, which this parser fills.
21
+ # Against an earlier one, building a snapshot fails on those arguments.
22
+ "span-panel-api>=3.6.0,<4.0",
20
23
  ]
21
24
 
22
25
  [project.urls]
@@ -423,13 +423,18 @@ class HomieDeviceConsumer:
423
423
  feed = self._acc.get_prop(pv_node, "feed")
424
424
  rel_pos = self._acc.get_prop(pv_node, "relative-position")
425
425
 
426
+ feed_circuit_id = normalize_circuit_id(feed) if feed else None
426
427
  return SpanPVSnapshot(
427
428
  vendor_name=vn if vn else None,
428
429
  model=pn if pn else None,
429
430
  software_version=sw if sw else None,
430
431
  nameplate_capacity_w=_parse_float(nc) if nc else None,
431
- feed_circuit_id=normalize_circuit_id(feed) if feed else None,
432
+ feed_circuit_id=feed_circuit_id,
432
433
  relative_position=rel_pos.upper() if rel_pos else None,
434
+ device_id=pv_node,
435
+ # The same key schema_1 gives this inverter, so a consumer's handle
436
+ # for it survives the panel's upgrade to the parent/child schema.
437
+ node_id=feed_circuit_id or pv_node,
433
438
  )
434
439
 
435
440
  def _build_evse_devices(self) -> dict[str, SpanEvseSnapshot]:
@@ -726,5 +731,8 @@ class HomieDeviceConsumer:
726
731
  circuits=circuits,
727
732
  battery=battery,
728
733
  pv=pv,
734
+ # Flat publishes one inverter at most, so this is `pv` again, keyed
735
+ # as schema_1 keys each of its inverters.
736
+ pv_inverters={pv.node_id: pv} if pv.node_id is not None else {},
729
737
  evse=evse,
730
738
  )
@@ -0,0 +1,420 @@
1
+ {
2
+ "firmwareVersion": "spanos2/r202603/05",
3
+ "homieDomain": "ebus",
4
+ "homieVersion": 5,
5
+ "types": {
6
+ "energy.ebus.device.distribution-enclosure.core": {
7
+ "vendor-name": {
8
+ "name": "Vendor name",
9
+ "datatype": "string"
10
+ },
11
+ "serial-number": {
12
+ "name": "Serial number",
13
+ "datatype": "string"
14
+ },
15
+ "hardware-version": {
16
+ "name": "Hardware version",
17
+ "datatype": "string"
18
+ },
19
+ "software-version": {
20
+ "name": "Software version",
21
+ "datatype": "string"
22
+ },
23
+ "door": {
24
+ "name": "Door state",
25
+ "datatype": "enum",
26
+ "format": "UNKNOWN,OPEN,CLOSED"
27
+ },
28
+ "grid-islandable": {
29
+ "name": "Capable of operating with power while disconnected from the grid",
30
+ "datatype": "boolean"
31
+ },
32
+ "dominant-power-source": {
33
+ "name": "Current dominant power source, load-shedding trigger",
34
+ "datatype": "enum",
35
+ "format": "GRID,BATTERY,PV,GENERATOR,NONE,UNKNOWN",
36
+ "settable": true
37
+ },
38
+ "relay": {
39
+ "name": "Main relay",
40
+ "datatype": "enum",
41
+ "format": "UNKNOWN,OPEN,CLOSED"
42
+ },
43
+ "l1-voltage": {
44
+ "name": "L1 voltage",
45
+ "datatype": "float",
46
+ "unit": "V"
47
+ },
48
+ "l2-voltage": {
49
+ "name": "L2 voltage",
50
+ "datatype": "float",
51
+ "unit": "V"
52
+ },
53
+ "breaker-rating": {
54
+ "name": "Main breaker rating",
55
+ "datatype": "integer",
56
+ "unit": "A"
57
+ },
58
+ "ethernet": {
59
+ "name": "Is Ethernet network interface operational?",
60
+ "datatype": "boolean"
61
+ },
62
+ "wifi": {
63
+ "name": "Is Wi-Fi network interface operational?",
64
+ "datatype": "boolean"
65
+ },
66
+ "wifi-ssid": {
67
+ "name": "SSID to which Wi-Fi network interface is connected",
68
+ "datatype": "string"
69
+ },
70
+ "vendor-cloud": {
71
+ "name": "Device connected to vendor cloud?",
72
+ "datatype": "enum",
73
+ "format": "UNKNOWN,UNCONNECTED,CONNECTED"
74
+ },
75
+ "postal-code": {
76
+ "name": "Postal (Zip) code",
77
+ "datatype": "string"
78
+ },
79
+ "time-zone": {
80
+ "name": "Time zone",
81
+ "datatype": "string"
82
+ }
83
+ },
84
+ "energy.ebus.device.lugs": {
85
+ "direction": {
86
+ "name": "Lugs feed direction: upstream or downstream",
87
+ "datatype": "enum",
88
+ "format": "UPSTREAM,DOWNSTREAM"
89
+ },
90
+ "feed": {
91
+ "name": "Device the lugs are connected to, if known",
92
+ "datatype": "string"
93
+ },
94
+ "l1-current": {
95
+ "name": "L1 current",
96
+ "datatype": "float",
97
+ "unit": "A"
98
+ },
99
+ "l2-current": {
100
+ "name": "L2 current",
101
+ "datatype": "float",
102
+ "unit": "A"
103
+ },
104
+ "active-power": {
105
+ "name": "Active power",
106
+ "datatype": "float",
107
+ "unit": "W"
108
+ },
109
+ "imported-energy": {
110
+ "name": "Imported energy",
111
+ "datatype": "float",
112
+ "unit": "Wh"
113
+ },
114
+ "exported-energy": {
115
+ "name": "Exported energy",
116
+ "datatype": "float",
117
+ "unit": "Wh"
118
+ }
119
+ },
120
+ "energy.ebus.device.circuit": {
121
+ "name": {
122
+ "name": "Circuit name",
123
+ "datatype": "string"
124
+ },
125
+ "relay": {
126
+ "name": "Circuit relay state",
127
+ "datatype": "enum",
128
+ "format": "UNKNOWN,OPEN,CLOSED",
129
+ "settable": true
130
+ },
131
+ "relay-requester": {
132
+ "name": "Actor requesting the relay state",
133
+ "datatype": "enum",
134
+ "format": "UNKNOWN,NONE,BACKUP,USER,PCS,PCS_FAIL_SAFE,ALWAYS_ON,NEVER_BACKUP,INVERTER,FAULT"
135
+ },
136
+ "breaker-rating": {
137
+ "name": "Circuit breaker rating",
138
+ "datatype": "integer",
139
+ "unit": "A"
140
+ },
141
+ "current": {
142
+ "name": "Measured current",
143
+ "datatype": "float",
144
+ "unit": "A"
145
+ },
146
+ "active-power": {
147
+ "name": "Measured active power",
148
+ "datatype": "float",
149
+ "unit": "kW"
150
+ },
151
+ "imported-energy": {
152
+ "name": "Measured energy imported",
153
+ "datatype": "float",
154
+ "unit": "Wh"
155
+ },
156
+ "exported-energy": {
157
+ "name": "Measured energy exported",
158
+ "datatype": "float",
159
+ "unit": "Wh"
160
+ },
161
+ "space": {
162
+ "name": "Circuit breaker space number within load center",
163
+ "datatype": "integer",
164
+ "format": "1:32:1"
165
+ },
166
+ "dipole": {
167
+ "name": "Does circuit land on a two-pole breaker?",
168
+ "datatype": "boolean"
169
+ },
170
+ "shed-priority": {
171
+ "name": "Configured priority of circuit shedding when off-grid (dominant-power-source != GRID)",
172
+ "datatype": "enum",
173
+ "format": "UNKNOWN,OFF_GRID,SOC_THRESHOLD,NEVER",
174
+ "settable": true
175
+ },
176
+ "pcs-managed": {
177
+ "name": "Is circuit managed by PCS?",
178
+ "datatype": "boolean"
179
+ },
180
+ "pcs-priority": {
181
+ "name": "Circuit PCS priority ranking",
182
+ "datatype": "integer"
183
+ },
184
+ "sheddable": {
185
+ "name": "Is circuit configured to be sheddable?",
186
+ "datatype": "boolean"
187
+ },
188
+ "never-backup": {
189
+ "name": "Is circuit configured to be never-backup?",
190
+ "datatype": "boolean"
191
+ },
192
+ "always-on": {
193
+ "name": "Is circuit configured to be always on?",
194
+ "datatype": "boolean"
195
+ }
196
+ },
197
+ "energy.ebus.device.bess": {
198
+ "vendor-name": {
199
+ "name": "Vendor name",
200
+ "datatype": "string"
201
+ },
202
+ "product-name": {
203
+ "name": "Product name",
204
+ "datatype": "string"
205
+ },
206
+ "model": {
207
+ "name": "Model",
208
+ "datatype": "string"
209
+ },
210
+ "serial-number": {
211
+ "name": "Serial number",
212
+ "datatype": "string"
213
+ },
214
+ "software-version": {
215
+ "name": "Software version",
216
+ "datatype": "string"
217
+ },
218
+ "nameplate-capacity": {
219
+ "name": "Nameplate capacity",
220
+ "datatype": "float",
221
+ "unit": "kWh"
222
+ },
223
+ "relative-position": {
224
+ "name": "Relative position of the commissioned backup system WRT the distribution enclosure",
225
+ "datatype": "enum",
226
+ "format": "UPSTREAM,DOWNSTREAM,IN_PANEL"
227
+ },
228
+ "feed": {
229
+ "name": "Circuit ID upon which the commissioned backup system is landed",
230
+ "datatype": "enum"
231
+ },
232
+ "soc": {
233
+ "name": "State of charge",
234
+ "datatype": "float",
235
+ "unit": "%"
236
+ },
237
+ "soe": {
238
+ "name": "State of energy",
239
+ "datatype": "float",
240
+ "unit": "kWh"
241
+ },
242
+ "connected": {
243
+ "name": "Connected to backup system?",
244
+ "datatype": "boolean"
245
+ },
246
+ "grid-state": {
247
+ "name": "Grid connection state",
248
+ "datatype": "enum",
249
+ "format": "UNKNOWN,ON_GRID,OFF_GRID"
250
+ }
251
+ },
252
+ "energy.ebus.device.pv": {
253
+ "vendor-name": {
254
+ "name": "Vendor name",
255
+ "datatype": "string"
256
+ },
257
+ "product-name": {
258
+ "name": "Product name",
259
+ "datatype": "string"
260
+ },
261
+ "serial-number": {
262
+ "name": "Serial number",
263
+ "datatype": "string"
264
+ },
265
+ "software-version": {
266
+ "name": "Software version",
267
+ "datatype": "string"
268
+ },
269
+ "nameplate-capacity": {
270
+ "name": "Nameplate capacity",
271
+ "datatype": "float",
272
+ "unit": "W"
273
+ },
274
+ "relative-position": {
275
+ "name": "Relative position of the commissioned PV system WRT the distribution enclosure",
276
+ "datatype": "enum",
277
+ "format": "UPSTREAM,DOWNSTREAM,IN_PANEL"
278
+ },
279
+ "feed": {
280
+ "name": "Circuit ID upon which the commissioned PV system is landed",
281
+ "datatype": "enum"
282
+ }
283
+ },
284
+ "energy.ebus.device.evse": {
285
+ "vendor-name": {
286
+ "name": "Vendor name",
287
+ "datatype": "string"
288
+ },
289
+ "product-name": {
290
+ "name": "Product name",
291
+ "datatype": "string"
292
+ },
293
+ "part-number": {
294
+ "name": "Part number",
295
+ "datatype": "string"
296
+ },
297
+ "serial-number": {
298
+ "name": "Serial number",
299
+ "datatype": "string"
300
+ },
301
+ "software-version": {
302
+ "name": "Software version",
303
+ "datatype": "string"
304
+ },
305
+ "feed": {
306
+ "name": "Circuit ID upon which the commissioned EVSE is landed",
307
+ "datatype": "enum"
308
+ },
309
+ "lock-state": {
310
+ "name": "Lock state",
311
+ "datatype": "enum",
312
+ "format": "UNKNOWN,LOCKED,UNLOCKED"
313
+ },
314
+ "status": {
315
+ "name": "Status",
316
+ "datatype": "enum",
317
+ "format": "UNKNOWN,AVAILABLE,PREPARING,CHARGING,SUSPENDED_EV,SUSPENDED_EVSE,FINISHING,RESERVED,FAULTED,UNAVAILABLE"
318
+ },
319
+ "advertised-current": {
320
+ "name": "Current EVSE is advertising to the EV",
321
+ "datatype": "float",
322
+ "unit": "A"
323
+ }
324
+ },
325
+ "energy.ebus.device.pcs": {
326
+ "enabled": {
327
+ "name": "PCS system enabled",
328
+ "datatype": "boolean"
329
+ },
330
+ "active": {
331
+ "name": "PCS system actively controlling one (or more) loads",
332
+ "datatype": "boolean"
333
+ },
334
+ "import-limit": {
335
+ "name": "The power import limit currently being managed to",
336
+ "datatype": "float",
337
+ "unit": "A"
338
+ },
339
+ "feed-import-limit": {
340
+ "name": "Limit of maximum power feeding the distribution enclosure",
341
+ "datatype": "float",
342
+ "unit": "A"
343
+ },
344
+ "feed-import-limit-enablement": {
345
+ "name": "Enablement status of the feed-import-limit",
346
+ "datatype": "enum",
347
+ "format": "UNSPECIFIED,UNCONFIGURED,DISABLED,ENABLED"
348
+ },
349
+ "feed-import-limit-active": {
350
+ "name": "Is feed-import-limit currently being enforced?",
351
+ "datatype": "boolean"
352
+ },
353
+ "grid-import-limit": {
354
+ "name": "Grid limit maximum import power",
355
+ "datatype": "float",
356
+ "unit": "A"
357
+ },
358
+ "grid-import-limit-enablement": {
359
+ "name": "Enablement status of the grid-import-limit",
360
+ "datatype": "enum",
361
+ "format": "UNSPECIFIED,UNCONFIGURED,DISABLED,ENABLED"
362
+ },
363
+ "grid-import-limit-active": {
364
+ "name": "Is grid-import-limit currently being enforced?",
365
+ "datatype": "boolean"
366
+ },
367
+ "off-grid-import-limit": {
368
+ "name": "Off-Grid limit maximum import power",
369
+ "datatype": "float",
370
+ "unit": "A"
371
+ },
372
+ "off-grid-import-limit-enablement": {
373
+ "name": "Enablement status of the off-grid-import-limit",
374
+ "datatype": "enum",
375
+ "format": "UNSPECIFIED,UNCONFIGURED,DISABLED,ENABLED"
376
+ },
377
+ "off-grid-import-limit-active": {
378
+ "name": "Is off-grid-import-limit currently being enforced?",
379
+ "datatype": "boolean"
380
+ },
381
+ "requested-import-limit": {
382
+ "name": "Requested limit maximum import power",
383
+ "datatype": "float",
384
+ "unit": "A"
385
+ },
386
+ "requested-import-limit-enablement": {
387
+ "name": "Enablement status of the requested-import-limit",
388
+ "datatype": "enum",
389
+ "format": "UNSPECIFIED,UNCONFIGURED,DISABLED,ENABLED"
390
+ },
391
+ "requested-import-limit-active": {
392
+ "name": "Is requested-import-limit currently being enforced?",
393
+ "datatype": "boolean"
394
+ }
395
+ },
396
+ "energy.ebus.device.power-flows": {
397
+ "pv": {
398
+ "name": "PV power flow",
399
+ "datatype": "float",
400
+ "unit": "W"
401
+ },
402
+ "battery": {
403
+ "name": "Battery/BESS power flow",
404
+ "datatype": "float",
405
+ "unit": "W"
406
+ },
407
+ "grid": {
408
+ "name": "Grid power flow",
409
+ "datatype": "float",
410
+ "unit": "W"
411
+ },
412
+ "site": {
413
+ "name": "Site power flow",
414
+ "datatype": "float",
415
+ "unit": "W"
416
+ }
417
+ }
418
+ },
419
+ "typesSchemaHash": "sha256:d347556a07d98f40"
420
+ }