bricklogger-ibos 0.1.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.
- bricklogger_ibos-0.1.0/LICENSE +21 -0
- bricklogger_ibos-0.1.0/PKG-INFO +398 -0
- bricklogger_ibos-0.1.0/README.md +372 -0
- bricklogger_ibos-0.1.0/pyproject.toml +102 -0
- bricklogger_ibos-0.1.0/pyproject.toml.orig +73 -0
- bricklogger_ibos-0.1.0/src/bricklogger_ibos/__init__.py +2 -0
- bricklogger_ibos-0.1.0/src/bricklogger_ibos/client.py +240 -0
- bricklogger_ibos-0.1.0/src/bricklogger_ibos/config.py +143 -0
- bricklogger_ibos-0.1.0/src/bricklogger_ibos/declaration.py +84 -0
- bricklogger_ibos-0.1.0/src/bricklogger_ibos/ibos.ttl +104 -0
- bricklogger_ibos-0.1.0/src/bricklogger_ibos/references.py +241 -0
- bricklogger_ibos-0.1.0/src/bricklogger_ibos/source.py +577 -0
- bricklogger_ibos-0.1.0/src/bricklogger_ibos/tools.py +390 -0
- bricklogger_ibos-0.1.0/src/bricklogger_ibos/values.py +299 -0
- bricklogger_ibos-0.1.0/src/bricklogger_ibos/vocabulary.py +16 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CX1 ApS
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bricklogger-ibos
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Bricklogger source: the trend history of BACnet objects from the iBOS Data API
|
|
5
|
+
Keywords: brick,bacnet,building-automation,ibos,bricklogger
|
|
6
|
+
Author: Michael Rosenlund
|
|
7
|
+
Author-email: Michael Rosenlund <michael.rosenlund@cx1.dk>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
11
|
+
Classifier: Intended Audience :: System Administrators
|
|
12
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Home Automation
|
|
18
|
+
Requires-Dist: bricklogger>=0.2,<0.3
|
|
19
|
+
Requires-Dist: httpx>=0.27
|
|
20
|
+
Requires-Dist: bacpypes3>=0.0.90
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Project-URL: Homepage, https://github.com/CX1-ApS/bricklogger-ibos
|
|
23
|
+
Project-URL: Source, https://github.com/CX1-ApS/bricklogger-ibos
|
|
24
|
+
Project-URL: Issues, https://github.com/CX1-ApS/bricklogger-ibos/issues
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
|
|
27
|
+
# Bricklogger iBOS source
|
|
28
|
+
|
|
29
|
+
A source plugin for [Bricklogger](https://cx1-aps.github.io/bricklogger/) that collects the trend history of
|
|
30
|
+
BACnet objects from the **iBOS Data API**, the cloud service of iBOS
|
|
31
|
+
Technologies, for the points a Brick model gives an iBOS reference.
|
|
32
|
+
|
|
33
|
+
The API exposes the objects of a project's devices, with their trend history,
|
|
34
|
+
read-only over HTTPS, authenticated with a personal access token. It carries no
|
|
35
|
+
live values — what it serves is each object's trend history — so the source is
|
|
36
|
+
a **history source**: its collection method is `poll`, and every round fetches
|
|
37
|
+
the samples the cloud has received since the round before, each **with the
|
|
38
|
+
cloud's own timestamp**. The objects are the same objects a BACnet/IP source
|
|
39
|
+
would read on site, so a point may carry both a BACnet reference and an iBOS
|
|
40
|
+
reference; which source collects it follows from the source configuration, and
|
|
41
|
+
a point claimed by both is the validation error the
|
|
42
|
+
[architecture](https://cx1-aps.github.io/bricklogger/architecture/#which-source-owns-which-point) describes.
|
|
43
|
+
|
|
44
|
+
## Install
|
|
45
|
+
|
|
46
|
+
The plugin is installed into Bricklogger's environment, from the CLI, the web
|
|
47
|
+
interface's Plugins screen or, in a container, into the plugin volume:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
bricklogger plugins add bricklogger-ibos
|
|
51
|
+
sudo systemctl restart bricklogger # or: bricklogger daemon restart
|
|
52
|
+
bricklogger plugins ibos # the settings and tools it brings
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
| Plugin | Bricklogger |
|
|
56
|
+
|--------|-------------|
|
|
57
|
+
| 0.1 | 0.2 |
|
|
58
|
+
|
|
59
|
+
A plugin is built for one minor version of Bricklogger; after upgrading
|
|
60
|
+
Bricklogger across one, upgrade the plugin with the same `plugins add`.
|
|
61
|
+
|
|
62
|
+
Then put the token in Bricklogger's `env` file and add an instance — guided
|
|
63
|
+
with `bricklogger sources add ibos_main --type ibos`, or by hand:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
IBOS_PAT=...
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Coming from Bricklogger 0.1,** where the iBOS source was built in: the
|
|
70
|
+
configuration, the type name `ibos`, the vocabulary and the collected history
|
|
71
|
+
are unchanged. After the upgrade to Bricklogger 0.2 the instance shows as
|
|
72
|
+
failed until this plugin is added and the daemon restarted; it then resumes
|
|
73
|
+
from the latest observation of every point.
|
|
74
|
+
|
|
75
|
+
## Configuration
|
|
76
|
+
|
|
77
|
+
```yaml
|
|
78
|
+
# sources.yaml
|
|
79
|
+
ibos_main:
|
|
80
|
+
type: ibos
|
|
81
|
+
token: ${IBOS_PAT} # required: personal access token
|
|
82
|
+
url: https://api.data.ibostechnologies.com # default
|
|
83
|
+
projects: [4711, "0d3f5c2e-6a1b-4c8f-9e2d-7b5a1c3d4e6f"] # optional: claim scope
|
|
84
|
+
rate_limit: 50 # default: requests per second
|
|
85
|
+
backfill: 7d # default: history at first assignment
|
|
86
|
+
overlap: 1h # default: window re-read for late samples
|
|
87
|
+
timeout: 30s # default: per request
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
| Key | Requirement | Content |
|
|
91
|
+
|-----|-------------|---------|
|
|
92
|
+
| `token` | required | The personal access token the instance authenticates with, given as `${IBOS_PAT}` with the value in Bricklogger's [`env` file](https://cx1-aps.github.io/bricklogger/features/configuration/#the-env-file) like every secret |
|
|
93
|
+
| `url` | default `https://api.data.ibostechnologies.com` | The API's base URL |
|
|
94
|
+
| `projects` | optional | The projects the instance claims: a list of project numbers and UUIDs, matched against each reference's project node |
|
|
95
|
+
| `rate_limit` | default `50` | The instance's request budget in requests per second. The API allows 100 per token, and a token may be shared with other clients |
|
|
96
|
+
| `backfill` | default `7d` | How far back the first fetch of a point reaches; `0` fetches only what the cloud receives after the assignment |
|
|
97
|
+
| `overlap` | default `1h` | How far behind the latest known sample every round starts again, to catch samples the cloud received late |
|
|
98
|
+
| `timeout` | default `30s` | How long one request waits for its response |
|
|
99
|
+
|
|
100
|
+
A reference is in scope when its project's number or UUID is in `projects`.
|
|
101
|
+
Without `projects` the instance claims every iBOS reference — the simple
|
|
102
|
+
installation configures nothing but the token. The instance declares no
|
|
103
|
+
exclusive [resource](https://cx1-aps.github.io/bricklogger/architecture/#isolation-and-resources): two
|
|
104
|
+
instances with the same token and different projects are a legitimate way to
|
|
105
|
+
split a budget.
|
|
106
|
+
|
|
107
|
+
## The reference
|
|
108
|
+
|
|
109
|
+
A point is addressed through Brick's external reference, as for every source:
|
|
110
|
+
the point carries `ref:hasExternalReference` to a reference node. Brick's
|
|
111
|
+
reference schema has no type for an object in a cloud service, so the
|
|
112
|
+
reference is written in the **iBOS vocabulary** described
|
|
113
|
+
[below](#the-ibos-vocabulary), namespace `https://brick.cx2.dk/schema/ibos#`
|
|
114
|
+
with the prefix `ibos:`:
|
|
115
|
+
|
|
116
|
+
```turtle
|
|
117
|
+
@prefix ibos: <https://brick.cx2.dk/schema/ibos#> .
|
|
118
|
+
|
|
119
|
+
ex:Building_A a rec:Building, ibos:Project ;
|
|
120
|
+
ibos:project-id 4711 ;
|
|
121
|
+
ibos:project-uuid "0d3f5c2e-6a1b-4c8f-9e2d-7b5a1c3d4e6f" . # optional
|
|
122
|
+
|
|
123
|
+
ex:AHU_01_SAT a brick:Supply_Air_Temperature_Sensor ;
|
|
124
|
+
ref:hasExternalReference [
|
|
125
|
+
a ibos:Reference ;
|
|
126
|
+
ibos:object-uuid "3fa85f64-5717-4562-b3fc-2c963f66afa6" ;
|
|
127
|
+
ibos:project ex:Building_A ;
|
|
128
|
+
ibos:object-type "analog-input" ; # informational, checked
|
|
129
|
+
ibos:object-instance 3 ; # informational, checked
|
|
130
|
+
ibos:object-name "AHU01_SAT" ; # informational
|
|
131
|
+
ibos:device-uuid "9b2c7d1e-4f3a-4b5c-8d6e-1a2b3c4d5e6f" # informational
|
|
132
|
+
] .
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
- **The object:** `ibos:object-uuid` is the object's UUID in iBOS, the key
|
|
136
|
+
under which the API serves everything about it. The source recognises an
|
|
137
|
+
iBOS reference by this property, whether or not the node is typed
|
|
138
|
+
`ibos:Reference`: the vocabulary's rule derives the type at activation, as
|
|
139
|
+
Brick's own rules do for Brick's reference types.
|
|
140
|
+
- **The project:** `ibos:project` links the reference to the iBOS project the
|
|
141
|
+
object belongs to, a node with `ibos:project-id`, the project's number in
|
|
142
|
+
iBOS. The node is typically the building itself, typed `rec:Building` and
|
|
143
|
+
`ibos:Project` at once; `ibos:project-uuid` and a label are optional. The
|
|
144
|
+
project is the unit by which an instance decides which points it claims.
|
|
145
|
+
- **Informational properties:** `ibos:object-type`, in the standard's
|
|
146
|
+
hyphenated spelling, `ibos:object-instance`, `ibos:object-name` and
|
|
147
|
+
`ibos:device-uuid` describe the object as iBOS knows it. They play no part
|
|
148
|
+
in addressing, but the source reads the object's metadata from the API in
|
|
149
|
+
any case, and a type or instance number in the graph that contradicts the
|
|
150
|
+
API's makes the point `rejected`: the reference then almost certainly
|
|
151
|
+
carries the wrong UUID.
|
|
152
|
+
|
|
153
|
+
**What is rejected.** As for every source, a reference the source recognises
|
|
154
|
+
but cannot use gives the point the outcome `rejected` with a reason that
|
|
155
|
+
names the problem, and the point is not collected until the model is fixed.
|
|
156
|
+
That covers a node typed `ibos:Reference` without `ibos:object-uuid`, a
|
|
157
|
+
malformed UUID or instance number, a reference without `ibos:project`, a
|
|
158
|
+
project node without `ibos:project-id`, a type or instance number that
|
|
159
|
+
contradicts the API, and a point with two iBOS references of which neither is
|
|
160
|
+
marked `ref:preferred true`
|
|
161
|
+
— where one is marked, it is used. The rule is the same as for BACnet: a
|
|
162
|
+
point logged from the wrong object under the right name is worse than a
|
|
163
|
+
visible gap.
|
|
164
|
+
|
|
165
|
+
> [!NOTE]
|
|
166
|
+
> **Not a time-series reference.** Brick's `ref:TimeseriesReference` says
|
|
167
|
+
> where a point's time series is **stored**. It is reserved for the databases
|
|
168
|
+
> Bricklogger writes to and is never a source's address, so an iBOS object is
|
|
169
|
+
> not written as one.
|
|
170
|
+
|
|
171
|
+
## The iBOS vocabulary
|
|
172
|
+
|
|
173
|
+
The vocabulary is a small extension of Brick's reference schema: one Turtle
|
|
174
|
+
document with the classes, properties and rules below, shipped in this
|
|
175
|
+
package. The namespace is an identifier; nothing needs to be fetched from it.
|
|
176
|
+
The iBOS source [declares it](https://cx1-aps.github.io/bricklogger/architecture/#declaration), so
|
|
177
|
+
the daemon loads it at every activation together with Brick's ontology and
|
|
178
|
+
rules — a model that uses it needs no network access — and the prefix `ibos:`
|
|
179
|
+
is pre-declared like `ref:` and `bacnet:`, in SPARQL and in prefixed names.
|
|
180
|
+
|
|
181
|
+
| Term | Kind | Content |
|
|
182
|
+
|------|------|---------|
|
|
183
|
+
| `ibos:Reference` | class, subclass of `ref:ExternalReference` | A reference to an object in iBOS |
|
|
184
|
+
| `ibos:Project` | class | An iBOS project; the node may also be the building or site itself |
|
|
185
|
+
| `ibos:object-uuid` | string | The object's UUID in iBOS. **Required** |
|
|
186
|
+
| `ibos:project` | link | The project the object belongs to. **Required** |
|
|
187
|
+
| `ibos:project-id` | integer | The project's number in iBOS, on the project node. **Required** |
|
|
188
|
+
| `ibos:project-uuid` | string | The project's UUID, on the project node. Optional |
|
|
189
|
+
| `ibos:object-type` | string | The BACnet object type in the standard's hyphenated spelling, e.g. `analog-input`. Informational, checked against the API |
|
|
190
|
+
| `ibos:object-instance` | integer | The BACnet instance number. Informational, checked against the API |
|
|
191
|
+
| `ibos:object-name` | string | The object's name in iBOS. Informational |
|
|
192
|
+
| `ibos:device-uuid` | string | The UUID of the device the object belongs to. Informational |
|
|
193
|
+
|
|
194
|
+
Two SHACL rules run at activation: a node under `ref:hasExternalReference`
|
|
195
|
+
that carries `ibos:object-uuid` is typed `ibos:Reference`, and the object of
|
|
196
|
+
`ibos:project` is typed `ibos:Project`.
|
|
197
|
+
|
|
198
|
+
## Fetching
|
|
199
|
+
|
|
200
|
+
The source owns the rhythm, within a budget:
|
|
201
|
+
|
|
202
|
+
- **A round per point.** At the rule's `interval` the source asks the cloud,
|
|
203
|
+
per object, for the samples newer than the latest it has, minus the
|
|
204
|
+
`overlap`. Samples already delivered are dropped in the source, so a
|
|
205
|
+
repeated sample never leaves it; a sample the cloud received late, with a
|
|
206
|
+
timestamp inside the window, is delivered when it appears. The `interval`
|
|
207
|
+
thus bounds how far the copy lags behind the cloud, not the spacing of the
|
|
208
|
+
samples — that is the cloud's, and every sample keeps its own timestamp.
|
|
209
|
+
- **Where a point starts.** A point the daemon has never recorded an
|
|
210
|
+
observation for — new in the model, or a fresh runtime state — is fetched
|
|
211
|
+
`backfill` back from that moment. A point it has an observation for — after
|
|
212
|
+
a restart of the daemon or of the instance — is fetched from that latest
|
|
213
|
+
observation minus `overlap`, however long ago, so a restart refetches
|
|
214
|
+
nothing but the overlap and an outage is caught up in pages; the
|
|
215
|
+
[assignment](https://cx1-aps.github.io/bricklogger/architecture/#operation) carries the timestamp. From then
|
|
216
|
+
on only what is new. A point with no new samples in a
|
|
217
|
+
round yields nothing: the age of its last value in status shows that the
|
|
218
|
+
cloud is silent.
|
|
219
|
+
- **Pages.** The API serves at most 10 000 samples per request, the newest
|
|
220
|
+
first; a longer stretch — the first fetch, or the catch-up after an outage
|
|
221
|
+
— is read in pages, each reaching further back than the one before, and
|
|
222
|
+
each counting against the budget.
|
|
223
|
+
- **The budget.** Requests are spread evenly under `rate_limit`, and nothing
|
|
224
|
+
else is configurable. A round takes at least as long as its requests need —
|
|
225
|
+
3 000 objects at 50 requests per second take a minute — and **rounds never
|
|
226
|
+
pile up**: the next round starts when the previous one has finished, and a
|
|
227
|
+
round that outlasts the interval gives the warning `rate_limited` per
|
|
228
|
+
instance, with the cadence the instance can actually keep. Nothing is lost
|
|
229
|
+
meanwhile, because the cloud keeps the history: the budget costs latency,
|
|
230
|
+
never samples. A `429` from the API is honoured — the source waits the
|
|
231
|
+
`Retry-After` it names — and counted in status.
|
|
232
|
+
|
|
233
|
+
Capacity follows from the budget: the points an instance serves, each divided
|
|
234
|
+
by its interval in seconds, must add up to less than `rate_limit` for the
|
|
235
|
+
rounds to keep their intervals — 50 requests per second serve 15 000 points
|
|
236
|
+
every five minutes, or 3 000 every minute.
|
|
237
|
+
|
|
238
|
+
**Timestamps are the cloud's.** Every observation carries the sample's
|
|
239
|
+
timestamp as the API gives it, in UTC; the source never stamps a sample
|
|
240
|
+
itself. An older sample can therefore arrive after a newer one, and the
|
|
241
|
+
daemon's *last known value* is the latest by timestamp, not by arrival, as the
|
|
242
|
+
[architecture](https://cx1-aps.github.io/bricklogger/architecture/#the-working-graph) describes.
|
|
243
|
+
|
|
244
|
+
## Value types and units
|
|
245
|
+
|
|
246
|
+
An object's type — `object_type` in the API, the BACnet object type — decides
|
|
247
|
+
the value type, with the same table as [BACnet/IP](https://cx1-aps.github.io/bricklogger/features/sources/#value-types-and-units).
|
|
248
|
+
A sample carries `value`, a number, and `value_text`, a text, and the type
|
|
249
|
+
says which of them is the value:
|
|
250
|
+
|
|
251
|
+
| Object type | Value type | Value and texts |
|
|
252
|
+
|-------------|------------|-----------------|
|
|
253
|
+
| analog-input, analog-output, analog-value, large-analog-value, loop | `number` | `value` |
|
|
254
|
+
| integer-value, positive-integer-value, accumulator | `integer` | `value`, as an integer |
|
|
255
|
+
| binary-input, binary-output, binary-value | `boolean` | `value` 1 is true, 0 false; a text in `value_text` that is not the number itself becomes the state's text |
|
|
256
|
+
| multi-state-input, multi-state-output, multi-state-value | `enum` | `value` is the state, counting from 1; a text in `value_text` that is not the number itself is learned as the state's text and delivered as metadata as it appears |
|
|
257
|
+
| character-string-value | `string` | `value_text` |
|
|
258
|
+
| date-time-value | `datetime` | `value_text`, as read; stored in UTC |
|
|
259
|
+
| Everything else | — | The point is `rejected`: the datatype has no counterpart in the vocabulary |
|
|
260
|
+
|
|
261
|
+
A sample whose `value` and `value_text` are both empty is delivered as `null`
|
|
262
|
+
with reason `no_value`: the cloud recorded that the collector had no value at
|
|
263
|
+
that time, and the time series says so. In the API as observed, `value_text`
|
|
264
|
+
repeats the number — `"3"` beside `3.0` — so states stay ordinals.
|
|
265
|
+
|
|
266
|
+
**Units.** The object's unit comes with its metadata from the API as
|
|
267
|
+
`unit_id`, BACnet's engineering-unit number, and as the text `units`, a
|
|
268
|
+
symbol such as `°C`, `%` or `m³/h`. The source translates the number into
|
|
269
|
+
Brick's unit vocabulary (QUDT) with the same table as BACnet/IP, translates
|
|
270
|
+
the symbol where it knows it — the objects list carries the symbol alone —
|
|
271
|
+
and otherwise delivers the text as the protocol's own designation. As for
|
|
272
|
+
every source, the graph's unit stays primary, a
|
|
273
|
+
disagreement gives the `unit_conflict` warning, and values are never
|
|
274
|
+
converted.
|
|
275
|
+
|
|
276
|
+
**When metadata is read.** The object's metadata — type, instance, name and
|
|
277
|
+
unit — is read from the API once when a point is assigned. It settles the
|
|
278
|
+
value type and the unit, and it is where a graph's `ibos:object-type` or
|
|
279
|
+
`ibos:object-instance` is checked; a contradiction gives `rejected`. The
|
|
280
|
+
enumeration's texts follow as the states appear in the data.
|
|
281
|
+
|
|
282
|
+
## Failed requests
|
|
283
|
+
|
|
284
|
+
The source delivers only what the cloud has recorded. When a request fails,
|
|
285
|
+
it delivers **nothing** for that point in that round and fetches the whole
|
|
286
|
+
stretch when the next request succeeds, so the time series stays complete and
|
|
287
|
+
carries no marks of the logger's own outages. What went wrong shows in
|
|
288
|
+
status, per project:
|
|
289
|
+
|
|
290
|
+
| Answer | Effect |
|
|
291
|
+
|--------|--------|
|
|
292
|
+
| `404` for the object | The point is `rejected` with the reason that the object is not in iBOS: the reference carries the wrong UUID, or the object is gone |
|
|
293
|
+
| `403` for the project | The project is unreachable in status with the error; its points stay `active` and receive nothing until the token has access again. A token's permissions are not a model error |
|
|
294
|
+
| `401` | The token is invalid: the instance is `failed` with the error, and the daemon restarts it with backoff |
|
|
295
|
+
| `429` | The source waits the `Retry-After` the API names and counts the answer; see [fetching](#fetching) |
|
|
296
|
+
| `5xx`, timeout, no connection | The project is unreachable in status until a request succeeds |
|
|
297
|
+
|
|
298
|
+
This is where a history source differs from a live one: BACnet/IP delivers
|
|
299
|
+
`null` with `unreachable` for every failed poll, because nothing else records
|
|
300
|
+
that the value was not available, whereas the cloud's history records it
|
|
301
|
+
itself.
|
|
302
|
+
|
|
303
|
+
## Protocol tools
|
|
304
|
+
|
|
305
|
+
The tools run with `bricklogger sources <instance> <tool>` — through the daemon
|
|
306
|
+
when it runs, in-process otherwise — as described for
|
|
307
|
+
[protocol tools](https://cx1-aps.github.io/bricklogger/architecture/#protocol-tools) in general. Every tool
|
|
308
|
+
returns a structured result that the CLI renders as a table or emits as JSON.
|
|
309
|
+
|
|
310
|
+
| Tool | Parameters | Result |
|
|
311
|
+
|------|------------|--------|
|
|
312
|
+
| `projects` | — | The projects the token has access to: number, UUID, name, description and the counts of devices and objects |
|
|
313
|
+
| `devices` | `--project`, number or UUID | The project's devices: UUID, device number, name, vendor, model and IP address |
|
|
314
|
+
| `objects` | `--device` UUID; `--values` adds the latest sample | The device's objects: UUID, type, instance, name, unit and its QUDT counterpart, optionally the latest sample |
|
|
315
|
+
| `read` | `--object` UUID; `--since` (default `1h`) and `--limit` (default `100`) bound the samples | The object's latest samples as the vocabulary sees them — timestamp, type and value — with the raw `value` and `value_text` beside |
|
|
316
|
+
| `resolve` | `--point`, a URI in full or prefixed form | How the point's reference resolves — object UUID, project and whether it is in the instance's scope — the object's metadata from the API against the graph's type and instance, and the latest sample, or the problem with the reference |
|
|
317
|
+
| `pointlist` | `--project`, number or UUID, optional | The point list: the projects the instance claims, or the one project, with their devices and the objects on each, as one JSON document to keep as a file |
|
|
318
|
+
|
|
319
|
+
`projects`, `devices`, `objects`, `read` and `pointlist` need nothing but the
|
|
320
|
+
instance's configuration, so they also run without a daemon. `resolve` needs
|
|
321
|
+
the running daemon, because the working graph is the daemon's. No tool writes
|
|
322
|
+
to the cloud; the API is read-only.
|
|
323
|
+
|
|
324
|
+
### The point list
|
|
325
|
+
|
|
326
|
+
The point list is what the API has, without a single sample: the projects,
|
|
327
|
+
their devices and the objects on each device — an inventory of what the token
|
|
328
|
+
can see, to file with the building's documentation or to hand to whoever
|
|
329
|
+
builds the Brick model. It is a [document](https://cx1-aps.github.io/bricklogger/architecture/#protocol-tools),
|
|
330
|
+
so the CLI writes it as JSON, to standard output or to a file with `-o`:
|
|
331
|
+
|
|
332
|
+
```
|
|
333
|
+
bricklogger sources ibos_main pointlist --project 4711 -o building-a.json
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
In the web interface the point list is offered on the `projects` listing: run
|
|
337
|
+
`projects`, and every project row carries a Download for that project's point
|
|
338
|
+
list, with a Download for the whole list — every project the instance claims —
|
|
339
|
+
above the table. The tool has no form of its own there.
|
|
340
|
+
|
|
341
|
+
Without `--project` the list covers the projects the instance claims — the
|
|
342
|
+
`projects` setting, or every project the token has access to when the setting
|
|
343
|
+
is absent. With `--project` it covers that one project, whether or not the
|
|
344
|
+
instance claims it. A project the token cannot see fails the tool, as it fails
|
|
345
|
+
`devices`.
|
|
346
|
+
|
|
347
|
+
```json
|
|
348
|
+
{
|
|
349
|
+
"instance": "ibos_main",
|
|
350
|
+
"url": "https://api.data.ibostechnologies.com",
|
|
351
|
+
"exported_at": "2026-09-09T13:05:12+00:00",
|
|
352
|
+
"counts": {"projects": 1, "devices": 1, "objects": 2},
|
|
353
|
+
"projects": [
|
|
354
|
+
{
|
|
355
|
+
"number": 4711,
|
|
356
|
+
"uuid": "0d3f5c2e-6a1b-4c8f-9e2d-7b5a1c3d4e6f",
|
|
357
|
+
"name": "Building A",
|
|
358
|
+
"description": null,
|
|
359
|
+
"devices": [
|
|
360
|
+
{
|
|
361
|
+
"uuid": "9b2c7d1e-4f3a-4b5c-8d6e-1a2b3c4d5e6f",
|
|
362
|
+
"number": 1201,
|
|
363
|
+
"name": "Supervisor",
|
|
364
|
+
"vendor": "Tridium",
|
|
365
|
+
"model": "JACE-8000",
|
|
366
|
+
"ip": "192.168.10.20",
|
|
367
|
+
"objects": [
|
|
368
|
+
{"uuid": "3fa85f64-5717-4562-b3fc-2c963f66afa6", "type": "analog-input", "instance": 3, "name": "AHU01_SAT", "unit": "°C", "qudt": "http://qudt.org/vocab/unit/DEG_C"},
|
|
369
|
+
{"uuid": "7c9e6679-7425-40de-944b-e07fc1f90ae7", "type": "binary-input", "instance": 1, "name": "AHU01_FAN", "unit": null, "qudt": null}
|
|
370
|
+
]
|
|
371
|
+
}
|
|
372
|
+
]
|
|
373
|
+
}
|
|
374
|
+
]
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
The header names the instance, the API's URL, the time of the export in UTC
|
|
379
|
+
and the counts; then follow the projects, each with its devices, each with its
|
|
380
|
+
objects. The fields are those of `projects`, `devices` and `objects` — a
|
|
381
|
+
project without its two counts, since the lists themselves are there — with
|
|
382
|
+
the object type in the standard's hyphenated spelling, so the document reads
|
|
383
|
+
as the three lists nested. Building it takes one request per project and one
|
|
384
|
+
per device, under the instance's budget as always; a project with thousands of
|
|
385
|
+
objects is a handful of requests.
|
|
386
|
+
|
|
387
|
+
## Reporting a problem
|
|
388
|
+
|
|
389
|
+
Bugs and questions go in the
|
|
390
|
+
[issues](https://github.com/CX1-ApS/bricklogger-ibos/issues). A security
|
|
391
|
+
vulnerability is reported privately, as
|
|
392
|
+
[SECURITY.md](https://github.com/CX1-ApS/bricklogger-ibos/blob/main/SECURITY.md)
|
|
393
|
+
describes.
|
|
394
|
+
|
|
395
|
+
## License
|
|
396
|
+
|
|
397
|
+
[MIT](https://github.com/CX1-ApS/bricklogger-ibos/blob/main/LICENSE). iBOS is a
|
|
398
|
+
product of iBOS Technologies; this plugin is not affiliated with it.
|