qapu-cli 0.7.0__tar.gz → 0.7.2__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.
- {qapu_cli-0.7.0 → qapu_cli-0.7.2}/PKG-INFO +250 -1
- {qapu_cli-0.7.0 → qapu_cli-0.7.2}/README.md +249 -0
- {qapu_cli-0.7.0 → qapu_cli-0.7.2}/pyproject.toml +1 -1
- {qapu_cli-0.7.0 → qapu_cli-0.7.2}/qapu_cli/main.py +161 -0
- {qapu_cli-0.7.0 → qapu_cli-0.7.2}/qapu_cli.egg-info/PKG-INFO +250 -1
- {qapu_cli-0.7.0 → qapu_cli-0.7.2}/qapu_cli/__init__.py +0 -0
- {qapu_cli-0.7.0 → qapu_cli-0.7.2}/qapu_cli/client.py +0 -0
- {qapu_cli-0.7.0 → qapu_cli-0.7.2}/qapu_cli.egg-info/SOURCES.txt +0 -0
- {qapu_cli-0.7.0 → qapu_cli-0.7.2}/qapu_cli.egg-info/dependency_links.txt +0 -0
- {qapu_cli-0.7.0 → qapu_cli-0.7.2}/qapu_cli.egg-info/entry_points.txt +0 -0
- {qapu_cli-0.7.0 → qapu_cli-0.7.2}/qapu_cli.egg-info/requires.txt +0 -0
- {qapu_cli-0.7.0 → qapu_cli-0.7.2}/qapu_cli.egg-info/top_level.txt +0 -0
- {qapu_cli-0.7.0 → qapu_cli-0.7.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qapu-cli
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.2
|
|
4
4
|
Summary: CLI client for the Qapu API - built for the Hermes agent, but usable by anyone talking to api.ovoo.com.tr from outside the Swarm.
|
|
5
5
|
Author: OVOO Technology
|
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -48,6 +48,236 @@ Either way installs a `qapu` command (see `pyproject.toml`'s `[project.scripts]`
|
|
|
48
48
|
|
|
49
49
|
## Commands
|
|
50
50
|
|
|
51
|
+
**Full command reference (100 commands, auto-generated from `main.py`'s docstrings on 2026-09-04 - regenerate the same way if this drifts, don't hand-maintain)**: every command below has `--help` for its full flag list; commands under a resource group (`device`, `variable`, `group`, ...) follow `qapu <group> <command> ...`. The rest of this section gives worked examples and deeper narrative for the commands with more going on (`data`/`trend`/`energy`/`voltage`/`current`/`power`/`ct-check`/`fleet`/`infrastructure`/etc.) - if a command below isn't mentioned again further down, its one-line description here plus `--help` is the whole story, there's nothing more to it.
|
|
52
|
+
|
|
53
|
+
#### Top-level (device-scoped or global)
|
|
54
|
+
|
|
55
|
+
| Command | Description |
|
|
56
|
+
|---|---|
|
|
57
|
+
| `qapu ct-check` | Diagnose CT (current transformer) wiring per phase - reversed polarity vs. wrong phase order - from live PF_R/S/T/AE_R/S/T (GET /hermes/data/{device_id}). |
|
|
58
|
+
| `qapu current` | Current synthesis parameters, grouped (RMS, imbalance, THD, crest factor, fundamental component) (GET /hermes/data/{device_id}). |
|
|
59
|
+
| `qapu data` | Readings per variable for a device - latest value, last N buffered readings (--last), daily min/avg/max (--days), or an explicit --start/--end range (GET /hermes/data/{device_id}). |
|
|
60
|
+
| `qapu energy` | One day's active/reactive energy breakdown per phase (GET /hermes/energy/{device_id}). |
|
|
61
|
+
| `qapu health` | Check CLI/server version, connectivity, and server status (GET /health, no auth required). |
|
|
62
|
+
| `qapu pipeline` | Fleet-wide pipeline throughput for a time window - ingest/data/rule/blockchain packet+event counts (GET /hermes/pipeline/stats). |
|
|
63
|
+
| `qapu power` | Power synthesis parameters, grouped (active/reactive/apparent, imbalance, power factor) (GET /hermes/data/{device_id}). |
|
|
64
|
+
| `qapu timeline` | Get one device's timeline events (GET /hermes/timeline/{device_id}). |
|
|
65
|
+
| `qapu trend` | Trend for one variable on one device: recent readings, daily min/avg/max, or an explicit --start/--end range (GET /hermes/trend/{device_id}/{variable_id}). |
|
|
66
|
+
| `qapu voltage` | Voltage synthesis parameters, grouped (RMS, imbalance, THD, fundamental component) (GET /hermes/data/{device_id}). |
|
|
67
|
+
|
|
68
|
+
#### `device` - device inventory
|
|
69
|
+
|
|
70
|
+
| Command | Description |
|
|
71
|
+
|---|---|
|
|
72
|
+
| `qapu device get` | Get one device's detail (GET /hermes/devices/{device_id}). |
|
|
73
|
+
| `qapu device list` | List every device Qapu knows about (GET /hermes/devices). |
|
|
74
|
+
|
|
75
|
+
#### `variable` - variable catalog
|
|
76
|
+
|
|
77
|
+
| Command | Description |
|
|
78
|
+
|---|---|
|
|
79
|
+
| `qapu variable get` | Get one variable's detail (GET /hermes/variables/{variable_id}). |
|
|
80
|
+
| `qapu variable list` | List the variable catalog (GET /hermes/variables). |
|
|
81
|
+
|
|
82
|
+
#### `group` - device groups
|
|
83
|
+
|
|
84
|
+
| Command | Description |
|
|
85
|
+
|---|---|
|
|
86
|
+
| `qapu group add` | Create a new device group (POST /hermes/groups). |
|
|
87
|
+
| `qapu group assign` | Assign a device to a group (POST /hermes/groups/{group_id}/devices/{device_id}). |
|
|
88
|
+
| `qapu group devices` | List the device IDs assigned to one group (GET /hermes/groups/{group_id}/devices). |
|
|
89
|
+
| `qapu group get` | Get one group's detail (GET /hermes/groups/{group_id}). |
|
|
90
|
+
| `qapu group list` | List device groups (GET /hermes/groups). |
|
|
91
|
+
| `qapu group unassign` | Remove a device's assignment from a group (DELETE /hermes/groups/{group_id}/devices/{device_id}). |
|
|
92
|
+
| `qapu group update` | Update an existing group - only the fields passed are changed (PUT /hermes/groups/{group_id}). |
|
|
93
|
+
|
|
94
|
+
#### `manufacturer` - manufacturer catalog
|
|
95
|
+
|
|
96
|
+
| Command | Description |
|
|
97
|
+
|---|---|
|
|
98
|
+
| `qapu manufacturer add` | Create a new manufacturer (POST /hermes/manufacturers). |
|
|
99
|
+
| `qapu manufacturer delete` | Delete an existing manufacturer (DELETE /hermes/manufacturers/{manufacturer_id}). |
|
|
100
|
+
| `qapu manufacturer get` | Get one manufacturer's detail (GET /hermes/manufacturers/{manufacturer_id}). |
|
|
101
|
+
| `qapu manufacturer list` | List the manufacturer catalog, grouped by category (GET /hermes/manufacturers). |
|
|
102
|
+
| `qapu manufacturer update` | Update an existing manufacturer - only the fields passed are changed (PUT /hermes/manufacturers/{manufacturer_id}). |
|
|
103
|
+
|
|
104
|
+
#### `model` - hardware model catalog
|
|
105
|
+
|
|
106
|
+
| Command | Description |
|
|
107
|
+
|---|---|
|
|
108
|
+
| `qapu model add` | Create a new hardware model, linked to its manufacturer (POST /hermes/models). |
|
|
109
|
+
| `qapu model delete` | Delete an existing hardware model (DELETE /hermes/models/{model_id}). |
|
|
110
|
+
| `qapu model get` | Get one hardware model's detail (GET /hermes/models/{model_id}). |
|
|
111
|
+
| `qapu model list` | List the hardware model catalog, grouped by the same thousands-as-category convention as manufacturers (GET /hermes/models). |
|
|
112
|
+
| `qapu model update` | Update an existing hardware model - only the fields passed are changed (PUT /hermes/models/{model_id}). |
|
|
113
|
+
|
|
114
|
+
#### `modem` - modem inventory
|
|
115
|
+
|
|
116
|
+
| Command | Description |
|
|
117
|
+
|---|---|
|
|
118
|
+
| `qapu modem get` | Get one modem's detail by IMEI (GET /hermes/modems/{imei}). |
|
|
119
|
+
| `qapu modem list` | List the modem inventory (GET /hermes/modems). |
|
|
120
|
+
|
|
121
|
+
#### `sim` - SIM inventory
|
|
122
|
+
|
|
123
|
+
| Command | Description |
|
|
124
|
+
|---|---|
|
|
125
|
+
| `qapu sim get` | Get one SIM's detail by ICCID (GET /hermes/sims/{iccid}). |
|
|
126
|
+
| `qapu sim list` | List the SIM inventory (GET /hermes/sims). |
|
|
127
|
+
|
|
128
|
+
#### `firmware` - firmware catalog
|
|
129
|
+
|
|
130
|
+
| Command | Description |
|
|
131
|
+
|---|---|
|
|
132
|
+
| `qapu firmware get` | Get one firmware version's detail (GET /hermes/firmware/{version}). |
|
|
133
|
+
| `qapu firmware list` | List the firmware catalog (GET /hermes/firmware). |
|
|
134
|
+
|
|
135
|
+
#### `status` - device/modem status catalog
|
|
136
|
+
|
|
137
|
+
| Command | Description |
|
|
138
|
+
|---|---|
|
|
139
|
+
| `qapu status add` | Create a new status (POST /hermes/statuses). |
|
|
140
|
+
| `qapu status delete` | Delete an existing status (DELETE /hermes/statuses/{status_id}). |
|
|
141
|
+
| `qapu status get` | Get one status's detail (GET /hermes/statuses/{status_id}). |
|
|
142
|
+
| `qapu status list` | List the device/modem status catalog, grouped by category (GET /hermes/statuses). |
|
|
143
|
+
| `qapu status update` | Update an existing status - only the fields passed are changed (PUT /hermes/statuses/{status_id}). |
|
|
144
|
+
|
|
145
|
+
#### `equipment-type` - box equipment type catalog
|
|
146
|
+
|
|
147
|
+
| Command | Description |
|
|
148
|
+
|---|---|
|
|
149
|
+
| `qapu equipment-type add` | Create a new equipment type - use before recording box equipment of a kind that doesn't exist in the catalog yet (POST /hermes/equipment_types). |
|
|
150
|
+
| `qapu equipment-type get` | Get one equipment type's detail (GET /hermes/equipment_types/{id}). |
|
|
151
|
+
| `qapu equipment-type list` | List the box equipment type catalog (GET /hermes/equipment_types). |
|
|
152
|
+
|
|
153
|
+
#### `pump-type` - pump type catalog
|
|
154
|
+
|
|
155
|
+
| Command | Description |
|
|
156
|
+
|---|---|
|
|
157
|
+
| `qapu pump-type add` | Create a new pump type - use before recording a pump of a kind that doesn't exist in the catalog yet (POST /hermes/pump_types). |
|
|
158
|
+
| `qapu pump-type delete` | Delete an existing pump type (DELETE /hermes/pump_types/{pump_type_id}). |
|
|
159
|
+
| `qapu pump-type get` | Get one pump type's detail (GET /hermes/pump_types/{id}). |
|
|
160
|
+
| `qapu pump-type list` | List the pump type catalog (GET /hermes/pump_types). |
|
|
161
|
+
| `qapu pump-type update` | Update an existing pump type - only the fields passed are changed (PUT /hermes/pump_types/{pump_type_id}). |
|
|
162
|
+
|
|
163
|
+
#### `infrastructure` - transformer/box/pump/equipment/land plot
|
|
164
|
+
|
|
165
|
+
| Command | Description |
|
|
166
|
+
|---|---|
|
|
167
|
+
| `qapu infrastructure add` | Create standalone infrastructure (transformer/electric box/equipment/pump), or add a land plot to a device's pump. Pick exactly one of --transformer/--electric-box/--equipment/--pump/--land-plot. |
|
|
168
|
+
| `qapu infrastructure assign` | Assign a device to control an existing pump, or monitor a transformer directly. Pick exactly one of --pump or --transformer. |
|
|
169
|
+
| `qapu infrastructure delete` | Delete one piece of box equipment (--equipment), or unlink/delete a device's whole infrastructure chain (DELETE /hermes/infrastructure/...). |
|
|
170
|
+
| `qapu infrastructure get` | Get one device's infrastructure - pump/box/transformer/equipment/land plots, or a direct transformer link (GET /hermes/infrastructure/{device_id}). |
|
|
171
|
+
| `qapu infrastructure update` | Update detail fields on a device's infrastructure. Pick exactly one of --transformer/--electric-box/--equipment/--pump/--land-plot. |
|
|
172
|
+
|
|
173
|
+
#### `blockchain` - mined block ledger
|
|
174
|
+
|
|
175
|
+
| Command | Description |
|
|
176
|
+
|---|---|
|
|
177
|
+
| `qapu blockchain get` | Get one device's block at a given index, full payload (GET /hermes/blockchain/{device_id}/{index}). |
|
|
178
|
+
| `qapu blockchain list` | List every mined block for a device, genesis to latest (GET /hermes/blockchain/{device_id}). |
|
|
179
|
+
| `qapu blockchain validate` | Validate a device's mined block chain - length, secure/tampered status, and time range (GET /hermes/blockchain/{device_id}/validate). |
|
|
180
|
+
|
|
181
|
+
#### `calibration` - Gain/Offset per variable
|
|
182
|
+
|
|
183
|
+
| Command | Description |
|
|
184
|
+
|---|---|
|
|
185
|
+
| `qapu calibration add` | Create a new calibration row for a device/variable (POST /hermes/calibration). |
|
|
186
|
+
| `qapu calibration delete` | Delete an existing calibration row (DELETE /hermes/calibration/{calibration_id}). |
|
|
187
|
+
| `qapu calibration get` | Get one calibration row's detail (GET /hermes/calibration/detail/{calibration_id}). |
|
|
188
|
+
| `qapu calibration list` | List a device's calibration rows - Gain/Offset per variable (GET /hermes/calibration/{device_id}). |
|
|
189
|
+
| `qapu calibration update` | Update an existing calibration row - only the fields passed are changed (PUT /hermes/calibration/{calibration_id}). |
|
|
190
|
+
|
|
191
|
+
#### `crop-type` - crop type catalog
|
|
192
|
+
|
|
193
|
+
| Command | Description |
|
|
194
|
+
|---|---|
|
|
195
|
+
| `qapu crop-type add` | Create a new crop type - use before recording a land plot growing a crop that doesn't exist in the catalog yet (POST /hermes/crop_types). |
|
|
196
|
+
| `qapu crop-type delete` | Delete an existing crop type (DELETE /hermes/crop_types/{crop_type_id}). |
|
|
197
|
+
| `qapu crop-type get` | Get one crop type's detail (GET /hermes/crop_types/{id}). |
|
|
198
|
+
| `qapu crop-type list` | List the crop type catalog (GET /hermes/crop_types). |
|
|
199
|
+
| `qapu crop-type update` | Update an existing crop type - only the fields passed are changed (PUT /hermes/crop_types/{crop_type_id}). |
|
|
200
|
+
|
|
201
|
+
#### `irrigation-type` - irrigation method catalog
|
|
202
|
+
|
|
203
|
+
| Command | Description |
|
|
204
|
+
|---|---|
|
|
205
|
+
| `qapu irrigation-type add` | Create a new irrigation type - use before recording a land plot using a method that doesn't exist in the catalog yet (POST /hermes/irrigation_types). |
|
|
206
|
+
| `qapu irrigation-type delete` | Delete an existing irrigation type (DELETE /hermes/irrigation_types/{irrigation_type_id}). |
|
|
207
|
+
| `qapu irrigation-type get` | Get one irrigation type's detail (GET /hermes/irrigation_types/{id}). |
|
|
208
|
+
| `qapu irrigation-type list` | List the irrigation type catalog (GET /hermes/irrigation_types). |
|
|
209
|
+
| `qapu irrigation-type update` | Update an existing irrigation type - only the fields passed are changed (PUT /hermes/irrigation_types/{irrigation_type_id}). |
|
|
210
|
+
|
|
211
|
+
#### `setting` - rule thresholds, register, electric box
|
|
212
|
+
|
|
213
|
+
| Command | Description |
|
|
214
|
+
|---|---|
|
|
215
|
+
| `qapu setting get` | Get a device's editable settings - thresholds, register, electric box (GET /hermes/setting/{device_id}). |
|
|
216
|
+
| `qapu setting update` | Update a device's editable settings - only the fields passed are changed (PUT /hermes/setting/{device_id}). |
|
|
217
|
+
|
|
218
|
+
#### `stream` - ingested packet history
|
|
219
|
+
|
|
220
|
+
| Command | Description |
|
|
221
|
+
|---|---|
|
|
222
|
+
| `qapu stream get` | Get one stream row's detail - command name and variable count included (GET /hermes/stream/detail/{stream_id}). |
|
|
223
|
+
| `qapu stream list` | List a device's ingested packet stream history, most recent first (GET /hermes/stream/{device_id}). |
|
|
224
|
+
|
|
225
|
+
#### `synthesis` - derived-metric calculation rules
|
|
226
|
+
|
|
227
|
+
| Command | Description |
|
|
228
|
+
|---|---|
|
|
229
|
+
| `qapu synthesis add` | Create a new synthesis rule (POST /hermes/synthesis). |
|
|
230
|
+
| `qapu synthesis delete` | Delete an existing synthesis rule (DELETE /hermes/synthesis/{rule_id}). |
|
|
231
|
+
| `qapu synthesis get` | Get one synthesis rule's detail (GET /hermes/synthesis/{rule_id}). |
|
|
232
|
+
| `qapu synthesis list` | List derived-metric calculation rules (GET /hermes/synthesis). |
|
|
233
|
+
| `qapu synthesis update` | Update an existing synthesis rule - only the fields passed are changed (PUT /hermes/synthesis/{rule_id}). |
|
|
234
|
+
|
|
235
|
+
#### `user-role` - user role catalog (Admin/User/...)
|
|
236
|
+
|
|
237
|
+
| Command | Description |
|
|
238
|
+
|---|---|
|
|
239
|
+
| `qapu user-role add` | Create a new user role (POST /hermes/user_roles). |
|
|
240
|
+
| `qapu user-role delete` | Delete an existing user role (DELETE /hermes/user_roles/{role_id}). |
|
|
241
|
+
| `qapu user-role get` | Get one user role's detail (GET /hermes/user_roles/{role_id}). |
|
|
242
|
+
| `qapu user-role list` | List the user role catalog, e.g. Admin/User (GET /hermes/user_roles). |
|
|
243
|
+
| `qapu user-role update` | Update an existing user role - only the fields passed are changed (PUT /hermes/user_roles/{role_id}). |
|
|
244
|
+
|
|
245
|
+
#### `irrigation` - irrigation event history
|
|
246
|
+
|
|
247
|
+
| Command | Description |
|
|
248
|
+
|---|---|
|
|
249
|
+
| `qapu irrigation abstract` | Get a device's irrigation summary - last irrigation + 24h/today/30d/month totals (GET /hermes/irrigation/{device_id}/abstract). |
|
|
250
|
+
| `qapu irrigation list` | List a device's irrigation event history - start/end/duration (GET /hermes/irrigation/{device_id}). |
|
|
251
|
+
|
|
252
|
+
#### `project` - project catalog (Cinga, WeatherStat, ...)
|
|
253
|
+
|
|
254
|
+
| Command | Description |
|
|
255
|
+
|---|---|
|
|
256
|
+
| `qapu project add` | Create a new project (POST /hermes/projects). |
|
|
257
|
+
| `qapu project delete` | Delete an existing project (DELETE /hermes/projects/{project_id}). |
|
|
258
|
+
| `qapu project devices` | List every device belonging to one project, full detail (GET /hermes/projects/{project_id}/devices). |
|
|
259
|
+
| `qapu project get` | Get one project's detail (GET /hermes/projects/{project_id}). |
|
|
260
|
+
| `qapu project list` | List the project catalog (GET /hermes/projects). |
|
|
261
|
+
| `qapu project update` | Update an existing project - only the fields passed are changed (PUT /hermes/projects/{project_id}). |
|
|
262
|
+
|
|
263
|
+
#### `fleet` - bulk, multi-device queries
|
|
264
|
+
|
|
265
|
+
| Command | Description |
|
|
266
|
+
|---|---|
|
|
267
|
+
| `qapu fleet data` | Latest reading per variable, across every device in a group or project (GET /hermes/data/fleet). |
|
|
268
|
+
|
|
269
|
+
#### `device-command` - command template catalog (not actuation)
|
|
270
|
+
|
|
271
|
+
| Command | Description |
|
|
272
|
+
|---|---|
|
|
273
|
+
| `qapu device-command add` | Create a new device command template (POST /hermes/device_commands). |
|
|
274
|
+
| `qapu device-command delete` | Delete an existing device command template (DELETE /hermes/device_commands/{command_id}). |
|
|
275
|
+
| `qapu device-command get` | Get one device command template's detail, including its full payload Template (GET /hermes/device_commands/{id}). |
|
|
276
|
+
| `qapu device-command list` | List the device command template catalog (GET /hermes/device_commands). |
|
|
277
|
+
| `qapu device-command update` | Update an existing device command template - only the fields passed are changed (PUT /hermes/device_commands/{command_id}). |
|
|
278
|
+
|
|
279
|
+
### Worked examples and deeper detail
|
|
280
|
+
|
|
51
281
|
```bash
|
|
52
282
|
qapu --version # print the installed qapu-cli version and exit
|
|
53
283
|
qapu health # GET /health - no auth, connectivity + CLI/server version + PyPI update check
|
|
@@ -119,6 +349,25 @@ qapu power <device_id> # active/reactive/apparent power, imbalance,
|
|
|
119
349
|
```
|
|
120
350
|
Unlike `qapu energy`, these are **not** daily-by-default - voltage/current/power are instantaneous quantities, not cumulative meters, so there's no natural "daily total" the way energy has one (confirmed with the user before building: "zaman seçimsel olsun... saatlik günlük haftalık" was the ask, but a real hourly bucket doesn't exist anywhere in the cache today, only daily - so `--days N` reuses the exact same vocabulary `data`/`trend` already use rather than inventing a new period concept). Pure CLI-side grouping over the existing `/hermes/data/{device_id}` response - zero new backend logic, each command is a hardcoded `(section title, [Variable_ID, ...])` list in `tools/cli/qapu_cli/main.py` (`VOLTAGE_SECTIONS_COMPACT`/`_FULL_EXTRA`, same pattern for `CURRENT_`/`POWER_`), a section is silently skipped if none of its variables are in the device's reading list. Live-tested against real prod data: a single packet covering the full Voltage+Current+Power raw variable set sent to a disposable test device, all three commands' compact and `--full`/`--json`/`--days` modes confirmed rendering correctly, plus the device-not-found (404) path.
|
|
121
351
|
|
|
352
|
+
**`qapu ct-check` (added 2026-09-04)** - field CT (current transformer) wiring diagnostic, per direct field request ("cihazımız üzerinde 3 adet akım trafosu girişi var... akım trafosu yönleri gibi durumlar oluyor... bunları analiz etmek için bir mekanizma kurmalıyız"):
|
|
353
|
+
```bash
|
|
354
|
+
qapu ct-check <device_id> # visual panel, one box per phase (R/S/T), colored red if flagged
|
|
355
|
+
qapu ct-check <device_id> --json
|
|
356
|
+
```
|
|
357
|
+
Two distinct real installation faults, both diagnosable from `PF_R/S/T` alone (`GET /hermes/data/{device_id}`, no new backend endpoint):
|
|
358
|
+
- **Reversed polarity** (a CT's own IN/OUT terminals swapped) - that phase's PF flips *sign* but keeps a magnitude close to the other phases' - the same signal the already-existing (not auto-seeded) `Rule.add_wrong_current_direction_rule()` checks via `AE_R/S/T < 0`, just available every packet instead of waiting for the cumulative meter to go negative.
|
|
359
|
+
- **Wrong phase order** (a CT clamped on the wrong line, e.g. R's current physically flowing through what's wired as the "S" channel) - that phase's PF *magnitude* itself looks wrong, since comparing the wrong voltage/current pair mixes in the ~120° phase separation between lines on top of the real load angle.
|
|
360
|
+
|
|
361
|
+
Both are heuristics, not certainties - flags a phase for physical inspection, doesn't replace it. Detection: baseline = **median** of all three phases' `|PF|` (not "average of the other two" - the first version tried that and broke under a real multi-fault test, since one bad phase polluted the baseline used to judge the *other*, genuinely healthy phases; the median is robust to a single outlier). `deviation = |own |PF|| - baseline|` - `>= 0.15` (`CT_CHECK_DEVIATION_THRESHOLD`) means "FAZ SIRASI ŞÜPHELİ", else a negative sign means "TERS BAĞLI (Polarite)", else "Normal". The 0.15 threshold is a reasonable rule of thumb (a healthy 3-phase load's phases normally track within a few percent of each other), not derived from real fault-case field data - revisit if a real miswiring case doesn't get flagged correctly. Each phase's panel also shows `AE` (cross-referencing the existing rule's own signal) and "son veri: X dk/saat önce" (via the same `_relative_time()` helper used elsewhere). Live-tested against real prod data via deliberately-crafted `PF_R/S/T` packets sent to a disposable test device: healthy (all three ≈0.93-0.94) → all Normal; single reversed-polarity phase → only that phase flagged, others stay Normal; single wrong-phase-order phase → only that phase flagged; **two simultaneous faults** (one reversed + one phase-order, the case that broke the first "average of other two" version) → all three phases correctly classified independently. Device left in a healthy state afterward.
|
|
362
|
+
|
|
363
|
+
**`qapu pipeline` (added 2026-09-04)** - fleet-wide pipeline throughput for a time window, per direct request ("son x süre içerisinde ingeste kaç paket gelmiş data ne kadar işlemiş rule ne işlemiş gibi... docker servis özetleri"):
|
|
364
|
+
```bash
|
|
365
|
+
qapu pipeline # last 60 minutes (default)
|
|
366
|
+
qapu pipeline --minutes 1440 # last 24 hours
|
|
367
|
+
qapu pipeline --json
|
|
368
|
+
```
|
|
369
|
+
Not device-scoped - a fleet-wide summary across every service in the `ingest -> stream -> calibration -> raw-writer -> synthesis -> ... -> rule` pipeline. Deliberately reads from the real tables each stage actually writes to (`raw_data.stream_time`/`valid_pack` for `hardware`'s ingest, `streams.stream_time` for `data`'s calibration+synthesis completion, `device_timeline.create_time` for `rule`'s observable output, `blockchain.create_time` for mined blocks) rather than any service's own `/health` endpoint - those are point-in-time snapshots (uptime, CPU, last-message-age), not a windowed throughput count. New `common/qapu_common/domain/pipeline.py` (`Pipeline_Stats.get_stats(minutes)`, four plain `SELECT count(*) ... WHERE <time column> >= now() - interval` queries, no caching - these are meant to be live) + `GET /hermes/pipeline/stats` (`services/api/src/routers/hermes.py`, `minutes` query param, 1 to 10080/7 days). **`Rule_Events` deliberately does NOT count rule evaluations** - `rule` evaluates every single stream message 1:1, the same count `Data_Processed` already reports, so repeating it would be redundant; it counts `device_timeline` rows instead, rule's actual observable output (a trigger/reset transition), not every evaluation pass. The CLI prints a bordered summary table plus a soft backlog hint (yellow note, not an error) if `Data_Processed` looks meaningfully behind `Ingest_Valid` (`<80%`) - a sign of consumer lag worth checking, not itself proof of one. Live-tested against real prod DB: `--minutes 60` correctly returned all zeros (no test traffic in the preceding hour at that point in the session), `--minutes 1440` correctly returned real 24h counts, and a fresh real packet sent immediately before a `--minutes 5` check showed up as exactly `+1` across Ingest/Data_Processed/Blockchain_Mined (Rule_Events stayed `0`, correctly - that packet's only variable, `PCB_T`, didn't cross any rule threshold).
|
|
370
|
+
|
|
122
371
|
`qapu trend` shows a per-variable history plus a simple linear-regression trend line (slope + direction). Three modes, picked by which options you pass:
|
|
123
372
|
- **Raw readings** (`--last N`, default `N=20`): the measurement cache's rolling buffer - bounded to its own depth (~50 most recent packets), *not* a time window. Good for "what's it doing right now."
|
|
124
373
|
- **Daily aggregates** (`--days N`): one row per calendar day (min/avg/max/count), from the same cache the `/measurement/{device_id}/history` endpoint reads - this is what actually covers multi-day/month windows, since the raw buffer doesn't go back that far. `--days` wins over `--last` if both are given (and `--start`/`--end` isn't).
|
|
@@ -36,6 +36,236 @@ Either way installs a `qapu` command (see `pyproject.toml`'s `[project.scripts]`
|
|
|
36
36
|
|
|
37
37
|
## Commands
|
|
38
38
|
|
|
39
|
+
**Full command reference (100 commands, auto-generated from `main.py`'s docstrings on 2026-09-04 - regenerate the same way if this drifts, don't hand-maintain)**: every command below has `--help` for its full flag list; commands under a resource group (`device`, `variable`, `group`, ...) follow `qapu <group> <command> ...`. The rest of this section gives worked examples and deeper narrative for the commands with more going on (`data`/`trend`/`energy`/`voltage`/`current`/`power`/`ct-check`/`fleet`/`infrastructure`/etc.) - if a command below isn't mentioned again further down, its one-line description here plus `--help` is the whole story, there's nothing more to it.
|
|
40
|
+
|
|
41
|
+
#### Top-level (device-scoped or global)
|
|
42
|
+
|
|
43
|
+
| Command | Description |
|
|
44
|
+
|---|---|
|
|
45
|
+
| `qapu ct-check` | Diagnose CT (current transformer) wiring per phase - reversed polarity vs. wrong phase order - from live PF_R/S/T/AE_R/S/T (GET /hermes/data/{device_id}). |
|
|
46
|
+
| `qapu current` | Current synthesis parameters, grouped (RMS, imbalance, THD, crest factor, fundamental component) (GET /hermes/data/{device_id}). |
|
|
47
|
+
| `qapu data` | Readings per variable for a device - latest value, last N buffered readings (--last), daily min/avg/max (--days), or an explicit --start/--end range (GET /hermes/data/{device_id}). |
|
|
48
|
+
| `qapu energy` | One day's active/reactive energy breakdown per phase (GET /hermes/energy/{device_id}). |
|
|
49
|
+
| `qapu health` | Check CLI/server version, connectivity, and server status (GET /health, no auth required). |
|
|
50
|
+
| `qapu pipeline` | Fleet-wide pipeline throughput for a time window - ingest/data/rule/blockchain packet+event counts (GET /hermes/pipeline/stats). |
|
|
51
|
+
| `qapu power` | Power synthesis parameters, grouped (active/reactive/apparent, imbalance, power factor) (GET /hermes/data/{device_id}). |
|
|
52
|
+
| `qapu timeline` | Get one device's timeline events (GET /hermes/timeline/{device_id}). |
|
|
53
|
+
| `qapu trend` | Trend for one variable on one device: recent readings, daily min/avg/max, or an explicit --start/--end range (GET /hermes/trend/{device_id}/{variable_id}). |
|
|
54
|
+
| `qapu voltage` | Voltage synthesis parameters, grouped (RMS, imbalance, THD, fundamental component) (GET /hermes/data/{device_id}). |
|
|
55
|
+
|
|
56
|
+
#### `device` - device inventory
|
|
57
|
+
|
|
58
|
+
| Command | Description |
|
|
59
|
+
|---|---|
|
|
60
|
+
| `qapu device get` | Get one device's detail (GET /hermes/devices/{device_id}). |
|
|
61
|
+
| `qapu device list` | List every device Qapu knows about (GET /hermes/devices). |
|
|
62
|
+
|
|
63
|
+
#### `variable` - variable catalog
|
|
64
|
+
|
|
65
|
+
| Command | Description |
|
|
66
|
+
|---|---|
|
|
67
|
+
| `qapu variable get` | Get one variable's detail (GET /hermes/variables/{variable_id}). |
|
|
68
|
+
| `qapu variable list` | List the variable catalog (GET /hermes/variables). |
|
|
69
|
+
|
|
70
|
+
#### `group` - device groups
|
|
71
|
+
|
|
72
|
+
| Command | Description |
|
|
73
|
+
|---|---|
|
|
74
|
+
| `qapu group add` | Create a new device group (POST /hermes/groups). |
|
|
75
|
+
| `qapu group assign` | Assign a device to a group (POST /hermes/groups/{group_id}/devices/{device_id}). |
|
|
76
|
+
| `qapu group devices` | List the device IDs assigned to one group (GET /hermes/groups/{group_id}/devices). |
|
|
77
|
+
| `qapu group get` | Get one group's detail (GET /hermes/groups/{group_id}). |
|
|
78
|
+
| `qapu group list` | List device groups (GET /hermes/groups). |
|
|
79
|
+
| `qapu group unassign` | Remove a device's assignment from a group (DELETE /hermes/groups/{group_id}/devices/{device_id}). |
|
|
80
|
+
| `qapu group update` | Update an existing group - only the fields passed are changed (PUT /hermes/groups/{group_id}). |
|
|
81
|
+
|
|
82
|
+
#### `manufacturer` - manufacturer catalog
|
|
83
|
+
|
|
84
|
+
| Command | Description |
|
|
85
|
+
|---|---|
|
|
86
|
+
| `qapu manufacturer add` | Create a new manufacturer (POST /hermes/manufacturers). |
|
|
87
|
+
| `qapu manufacturer delete` | Delete an existing manufacturer (DELETE /hermes/manufacturers/{manufacturer_id}). |
|
|
88
|
+
| `qapu manufacturer get` | Get one manufacturer's detail (GET /hermes/manufacturers/{manufacturer_id}). |
|
|
89
|
+
| `qapu manufacturer list` | List the manufacturer catalog, grouped by category (GET /hermes/manufacturers). |
|
|
90
|
+
| `qapu manufacturer update` | Update an existing manufacturer - only the fields passed are changed (PUT /hermes/manufacturers/{manufacturer_id}). |
|
|
91
|
+
|
|
92
|
+
#### `model` - hardware model catalog
|
|
93
|
+
|
|
94
|
+
| Command | Description |
|
|
95
|
+
|---|---|
|
|
96
|
+
| `qapu model add` | Create a new hardware model, linked to its manufacturer (POST /hermes/models). |
|
|
97
|
+
| `qapu model delete` | Delete an existing hardware model (DELETE /hermes/models/{model_id}). |
|
|
98
|
+
| `qapu model get` | Get one hardware model's detail (GET /hermes/models/{model_id}). |
|
|
99
|
+
| `qapu model list` | List the hardware model catalog, grouped by the same thousands-as-category convention as manufacturers (GET /hermes/models). |
|
|
100
|
+
| `qapu model update` | Update an existing hardware model - only the fields passed are changed (PUT /hermes/models/{model_id}). |
|
|
101
|
+
|
|
102
|
+
#### `modem` - modem inventory
|
|
103
|
+
|
|
104
|
+
| Command | Description |
|
|
105
|
+
|---|---|
|
|
106
|
+
| `qapu modem get` | Get one modem's detail by IMEI (GET /hermes/modems/{imei}). |
|
|
107
|
+
| `qapu modem list` | List the modem inventory (GET /hermes/modems). |
|
|
108
|
+
|
|
109
|
+
#### `sim` - SIM inventory
|
|
110
|
+
|
|
111
|
+
| Command | Description |
|
|
112
|
+
|---|---|
|
|
113
|
+
| `qapu sim get` | Get one SIM's detail by ICCID (GET /hermes/sims/{iccid}). |
|
|
114
|
+
| `qapu sim list` | List the SIM inventory (GET /hermes/sims). |
|
|
115
|
+
|
|
116
|
+
#### `firmware` - firmware catalog
|
|
117
|
+
|
|
118
|
+
| Command | Description |
|
|
119
|
+
|---|---|
|
|
120
|
+
| `qapu firmware get` | Get one firmware version's detail (GET /hermes/firmware/{version}). |
|
|
121
|
+
| `qapu firmware list` | List the firmware catalog (GET /hermes/firmware). |
|
|
122
|
+
|
|
123
|
+
#### `status` - device/modem status catalog
|
|
124
|
+
|
|
125
|
+
| Command | Description |
|
|
126
|
+
|---|---|
|
|
127
|
+
| `qapu status add` | Create a new status (POST /hermes/statuses). |
|
|
128
|
+
| `qapu status delete` | Delete an existing status (DELETE /hermes/statuses/{status_id}). |
|
|
129
|
+
| `qapu status get` | Get one status's detail (GET /hermes/statuses/{status_id}). |
|
|
130
|
+
| `qapu status list` | List the device/modem status catalog, grouped by category (GET /hermes/statuses). |
|
|
131
|
+
| `qapu status update` | Update an existing status - only the fields passed are changed (PUT /hermes/statuses/{status_id}). |
|
|
132
|
+
|
|
133
|
+
#### `equipment-type` - box equipment type catalog
|
|
134
|
+
|
|
135
|
+
| Command | Description |
|
|
136
|
+
|---|---|
|
|
137
|
+
| `qapu equipment-type add` | Create a new equipment type - use before recording box equipment of a kind that doesn't exist in the catalog yet (POST /hermes/equipment_types). |
|
|
138
|
+
| `qapu equipment-type get` | Get one equipment type's detail (GET /hermes/equipment_types/{id}). |
|
|
139
|
+
| `qapu equipment-type list` | List the box equipment type catalog (GET /hermes/equipment_types). |
|
|
140
|
+
|
|
141
|
+
#### `pump-type` - pump type catalog
|
|
142
|
+
|
|
143
|
+
| Command | Description |
|
|
144
|
+
|---|---|
|
|
145
|
+
| `qapu pump-type add` | Create a new pump type - use before recording a pump of a kind that doesn't exist in the catalog yet (POST /hermes/pump_types). |
|
|
146
|
+
| `qapu pump-type delete` | Delete an existing pump type (DELETE /hermes/pump_types/{pump_type_id}). |
|
|
147
|
+
| `qapu pump-type get` | Get one pump type's detail (GET /hermes/pump_types/{id}). |
|
|
148
|
+
| `qapu pump-type list` | List the pump type catalog (GET /hermes/pump_types). |
|
|
149
|
+
| `qapu pump-type update` | Update an existing pump type - only the fields passed are changed (PUT /hermes/pump_types/{pump_type_id}). |
|
|
150
|
+
|
|
151
|
+
#### `infrastructure` - transformer/box/pump/equipment/land plot
|
|
152
|
+
|
|
153
|
+
| Command | Description |
|
|
154
|
+
|---|---|
|
|
155
|
+
| `qapu infrastructure add` | Create standalone infrastructure (transformer/electric box/equipment/pump), or add a land plot to a device's pump. Pick exactly one of --transformer/--electric-box/--equipment/--pump/--land-plot. |
|
|
156
|
+
| `qapu infrastructure assign` | Assign a device to control an existing pump, or monitor a transformer directly. Pick exactly one of --pump or --transformer. |
|
|
157
|
+
| `qapu infrastructure delete` | Delete one piece of box equipment (--equipment), or unlink/delete a device's whole infrastructure chain (DELETE /hermes/infrastructure/...). |
|
|
158
|
+
| `qapu infrastructure get` | Get one device's infrastructure - pump/box/transformer/equipment/land plots, or a direct transformer link (GET /hermes/infrastructure/{device_id}). |
|
|
159
|
+
| `qapu infrastructure update` | Update detail fields on a device's infrastructure. Pick exactly one of --transformer/--electric-box/--equipment/--pump/--land-plot. |
|
|
160
|
+
|
|
161
|
+
#### `blockchain` - mined block ledger
|
|
162
|
+
|
|
163
|
+
| Command | Description |
|
|
164
|
+
|---|---|
|
|
165
|
+
| `qapu blockchain get` | Get one device's block at a given index, full payload (GET /hermes/blockchain/{device_id}/{index}). |
|
|
166
|
+
| `qapu blockchain list` | List every mined block for a device, genesis to latest (GET /hermes/blockchain/{device_id}). |
|
|
167
|
+
| `qapu blockchain validate` | Validate a device's mined block chain - length, secure/tampered status, and time range (GET /hermes/blockchain/{device_id}/validate). |
|
|
168
|
+
|
|
169
|
+
#### `calibration` - Gain/Offset per variable
|
|
170
|
+
|
|
171
|
+
| Command | Description |
|
|
172
|
+
|---|---|
|
|
173
|
+
| `qapu calibration add` | Create a new calibration row for a device/variable (POST /hermes/calibration). |
|
|
174
|
+
| `qapu calibration delete` | Delete an existing calibration row (DELETE /hermes/calibration/{calibration_id}). |
|
|
175
|
+
| `qapu calibration get` | Get one calibration row's detail (GET /hermes/calibration/detail/{calibration_id}). |
|
|
176
|
+
| `qapu calibration list` | List a device's calibration rows - Gain/Offset per variable (GET /hermes/calibration/{device_id}). |
|
|
177
|
+
| `qapu calibration update` | Update an existing calibration row - only the fields passed are changed (PUT /hermes/calibration/{calibration_id}). |
|
|
178
|
+
|
|
179
|
+
#### `crop-type` - crop type catalog
|
|
180
|
+
|
|
181
|
+
| Command | Description |
|
|
182
|
+
|---|---|
|
|
183
|
+
| `qapu crop-type add` | Create a new crop type - use before recording a land plot growing a crop that doesn't exist in the catalog yet (POST /hermes/crop_types). |
|
|
184
|
+
| `qapu crop-type delete` | Delete an existing crop type (DELETE /hermes/crop_types/{crop_type_id}). |
|
|
185
|
+
| `qapu crop-type get` | Get one crop type's detail (GET /hermes/crop_types/{id}). |
|
|
186
|
+
| `qapu crop-type list` | List the crop type catalog (GET /hermes/crop_types). |
|
|
187
|
+
| `qapu crop-type update` | Update an existing crop type - only the fields passed are changed (PUT /hermes/crop_types/{crop_type_id}). |
|
|
188
|
+
|
|
189
|
+
#### `irrigation-type` - irrigation method catalog
|
|
190
|
+
|
|
191
|
+
| Command | Description |
|
|
192
|
+
|---|---|
|
|
193
|
+
| `qapu irrigation-type add` | Create a new irrigation type - use before recording a land plot using a method that doesn't exist in the catalog yet (POST /hermes/irrigation_types). |
|
|
194
|
+
| `qapu irrigation-type delete` | Delete an existing irrigation type (DELETE /hermes/irrigation_types/{irrigation_type_id}). |
|
|
195
|
+
| `qapu irrigation-type get` | Get one irrigation type's detail (GET /hermes/irrigation_types/{id}). |
|
|
196
|
+
| `qapu irrigation-type list` | List the irrigation type catalog (GET /hermes/irrigation_types). |
|
|
197
|
+
| `qapu irrigation-type update` | Update an existing irrigation type - only the fields passed are changed (PUT /hermes/irrigation_types/{irrigation_type_id}). |
|
|
198
|
+
|
|
199
|
+
#### `setting` - rule thresholds, register, electric box
|
|
200
|
+
|
|
201
|
+
| Command | Description |
|
|
202
|
+
|---|---|
|
|
203
|
+
| `qapu setting get` | Get a device's editable settings - thresholds, register, electric box (GET /hermes/setting/{device_id}). |
|
|
204
|
+
| `qapu setting update` | Update a device's editable settings - only the fields passed are changed (PUT /hermes/setting/{device_id}). |
|
|
205
|
+
|
|
206
|
+
#### `stream` - ingested packet history
|
|
207
|
+
|
|
208
|
+
| Command | Description |
|
|
209
|
+
|---|---|
|
|
210
|
+
| `qapu stream get` | Get one stream row's detail - command name and variable count included (GET /hermes/stream/detail/{stream_id}). |
|
|
211
|
+
| `qapu stream list` | List a device's ingested packet stream history, most recent first (GET /hermes/stream/{device_id}). |
|
|
212
|
+
|
|
213
|
+
#### `synthesis` - derived-metric calculation rules
|
|
214
|
+
|
|
215
|
+
| Command | Description |
|
|
216
|
+
|---|---|
|
|
217
|
+
| `qapu synthesis add` | Create a new synthesis rule (POST /hermes/synthesis). |
|
|
218
|
+
| `qapu synthesis delete` | Delete an existing synthesis rule (DELETE /hermes/synthesis/{rule_id}). |
|
|
219
|
+
| `qapu synthesis get` | Get one synthesis rule's detail (GET /hermes/synthesis/{rule_id}). |
|
|
220
|
+
| `qapu synthesis list` | List derived-metric calculation rules (GET /hermes/synthesis). |
|
|
221
|
+
| `qapu synthesis update` | Update an existing synthesis rule - only the fields passed are changed (PUT /hermes/synthesis/{rule_id}). |
|
|
222
|
+
|
|
223
|
+
#### `user-role` - user role catalog (Admin/User/...)
|
|
224
|
+
|
|
225
|
+
| Command | Description |
|
|
226
|
+
|---|---|
|
|
227
|
+
| `qapu user-role add` | Create a new user role (POST /hermes/user_roles). |
|
|
228
|
+
| `qapu user-role delete` | Delete an existing user role (DELETE /hermes/user_roles/{role_id}). |
|
|
229
|
+
| `qapu user-role get` | Get one user role's detail (GET /hermes/user_roles/{role_id}). |
|
|
230
|
+
| `qapu user-role list` | List the user role catalog, e.g. Admin/User (GET /hermes/user_roles). |
|
|
231
|
+
| `qapu user-role update` | Update an existing user role - only the fields passed are changed (PUT /hermes/user_roles/{role_id}). |
|
|
232
|
+
|
|
233
|
+
#### `irrigation` - irrigation event history
|
|
234
|
+
|
|
235
|
+
| Command | Description |
|
|
236
|
+
|---|---|
|
|
237
|
+
| `qapu irrigation abstract` | Get a device's irrigation summary - last irrigation + 24h/today/30d/month totals (GET /hermes/irrigation/{device_id}/abstract). |
|
|
238
|
+
| `qapu irrigation list` | List a device's irrigation event history - start/end/duration (GET /hermes/irrigation/{device_id}). |
|
|
239
|
+
|
|
240
|
+
#### `project` - project catalog (Cinga, WeatherStat, ...)
|
|
241
|
+
|
|
242
|
+
| Command | Description |
|
|
243
|
+
|---|---|
|
|
244
|
+
| `qapu project add` | Create a new project (POST /hermes/projects). |
|
|
245
|
+
| `qapu project delete` | Delete an existing project (DELETE /hermes/projects/{project_id}). |
|
|
246
|
+
| `qapu project devices` | List every device belonging to one project, full detail (GET /hermes/projects/{project_id}/devices). |
|
|
247
|
+
| `qapu project get` | Get one project's detail (GET /hermes/projects/{project_id}). |
|
|
248
|
+
| `qapu project list` | List the project catalog (GET /hermes/projects). |
|
|
249
|
+
| `qapu project update` | Update an existing project - only the fields passed are changed (PUT /hermes/projects/{project_id}). |
|
|
250
|
+
|
|
251
|
+
#### `fleet` - bulk, multi-device queries
|
|
252
|
+
|
|
253
|
+
| Command | Description |
|
|
254
|
+
|---|---|
|
|
255
|
+
| `qapu fleet data` | Latest reading per variable, across every device in a group or project (GET /hermes/data/fleet). |
|
|
256
|
+
|
|
257
|
+
#### `device-command` - command template catalog (not actuation)
|
|
258
|
+
|
|
259
|
+
| Command | Description |
|
|
260
|
+
|---|---|
|
|
261
|
+
| `qapu device-command add` | Create a new device command template (POST /hermes/device_commands). |
|
|
262
|
+
| `qapu device-command delete` | Delete an existing device command template (DELETE /hermes/device_commands/{command_id}). |
|
|
263
|
+
| `qapu device-command get` | Get one device command template's detail, including its full payload Template (GET /hermes/device_commands/{id}). |
|
|
264
|
+
| `qapu device-command list` | List the device command template catalog (GET /hermes/device_commands). |
|
|
265
|
+
| `qapu device-command update` | Update an existing device command template - only the fields passed are changed (PUT /hermes/device_commands/{command_id}). |
|
|
266
|
+
|
|
267
|
+
### Worked examples and deeper detail
|
|
268
|
+
|
|
39
269
|
```bash
|
|
40
270
|
qapu --version # print the installed qapu-cli version and exit
|
|
41
271
|
qapu health # GET /health - no auth, connectivity + CLI/server version + PyPI update check
|
|
@@ -107,6 +337,25 @@ qapu power <device_id> # active/reactive/apparent power, imbalance,
|
|
|
107
337
|
```
|
|
108
338
|
Unlike `qapu energy`, these are **not** daily-by-default - voltage/current/power are instantaneous quantities, not cumulative meters, so there's no natural "daily total" the way energy has one (confirmed with the user before building: "zaman seçimsel olsun... saatlik günlük haftalık" was the ask, but a real hourly bucket doesn't exist anywhere in the cache today, only daily - so `--days N` reuses the exact same vocabulary `data`/`trend` already use rather than inventing a new period concept). Pure CLI-side grouping over the existing `/hermes/data/{device_id}` response - zero new backend logic, each command is a hardcoded `(section title, [Variable_ID, ...])` list in `tools/cli/qapu_cli/main.py` (`VOLTAGE_SECTIONS_COMPACT`/`_FULL_EXTRA`, same pattern for `CURRENT_`/`POWER_`), a section is silently skipped if none of its variables are in the device's reading list. Live-tested against real prod data: a single packet covering the full Voltage+Current+Power raw variable set sent to a disposable test device, all three commands' compact and `--full`/`--json`/`--days` modes confirmed rendering correctly, plus the device-not-found (404) path.
|
|
109
339
|
|
|
340
|
+
**`qapu ct-check` (added 2026-09-04)** - field CT (current transformer) wiring diagnostic, per direct field request ("cihazımız üzerinde 3 adet akım trafosu girişi var... akım trafosu yönleri gibi durumlar oluyor... bunları analiz etmek için bir mekanizma kurmalıyız"):
|
|
341
|
+
```bash
|
|
342
|
+
qapu ct-check <device_id> # visual panel, one box per phase (R/S/T), colored red if flagged
|
|
343
|
+
qapu ct-check <device_id> --json
|
|
344
|
+
```
|
|
345
|
+
Two distinct real installation faults, both diagnosable from `PF_R/S/T` alone (`GET /hermes/data/{device_id}`, no new backend endpoint):
|
|
346
|
+
- **Reversed polarity** (a CT's own IN/OUT terminals swapped) - that phase's PF flips *sign* but keeps a magnitude close to the other phases' - the same signal the already-existing (not auto-seeded) `Rule.add_wrong_current_direction_rule()` checks via `AE_R/S/T < 0`, just available every packet instead of waiting for the cumulative meter to go negative.
|
|
347
|
+
- **Wrong phase order** (a CT clamped on the wrong line, e.g. R's current physically flowing through what's wired as the "S" channel) - that phase's PF *magnitude* itself looks wrong, since comparing the wrong voltage/current pair mixes in the ~120° phase separation between lines on top of the real load angle.
|
|
348
|
+
|
|
349
|
+
Both are heuristics, not certainties - flags a phase for physical inspection, doesn't replace it. Detection: baseline = **median** of all three phases' `|PF|` (not "average of the other two" - the first version tried that and broke under a real multi-fault test, since one bad phase polluted the baseline used to judge the *other*, genuinely healthy phases; the median is robust to a single outlier). `deviation = |own |PF|| - baseline|` - `>= 0.15` (`CT_CHECK_DEVIATION_THRESHOLD`) means "FAZ SIRASI ŞÜPHELİ", else a negative sign means "TERS BAĞLI (Polarite)", else "Normal". The 0.15 threshold is a reasonable rule of thumb (a healthy 3-phase load's phases normally track within a few percent of each other), not derived from real fault-case field data - revisit if a real miswiring case doesn't get flagged correctly. Each phase's panel also shows `AE` (cross-referencing the existing rule's own signal) and "son veri: X dk/saat önce" (via the same `_relative_time()` helper used elsewhere). Live-tested against real prod data via deliberately-crafted `PF_R/S/T` packets sent to a disposable test device: healthy (all three ≈0.93-0.94) → all Normal; single reversed-polarity phase → only that phase flagged, others stay Normal; single wrong-phase-order phase → only that phase flagged; **two simultaneous faults** (one reversed + one phase-order, the case that broke the first "average of other two" version) → all three phases correctly classified independently. Device left in a healthy state afterward.
|
|
350
|
+
|
|
351
|
+
**`qapu pipeline` (added 2026-09-04)** - fleet-wide pipeline throughput for a time window, per direct request ("son x süre içerisinde ingeste kaç paket gelmiş data ne kadar işlemiş rule ne işlemiş gibi... docker servis özetleri"):
|
|
352
|
+
```bash
|
|
353
|
+
qapu pipeline # last 60 minutes (default)
|
|
354
|
+
qapu pipeline --minutes 1440 # last 24 hours
|
|
355
|
+
qapu pipeline --json
|
|
356
|
+
```
|
|
357
|
+
Not device-scoped - a fleet-wide summary across every service in the `ingest -> stream -> calibration -> raw-writer -> synthesis -> ... -> rule` pipeline. Deliberately reads from the real tables each stage actually writes to (`raw_data.stream_time`/`valid_pack` for `hardware`'s ingest, `streams.stream_time` for `data`'s calibration+synthesis completion, `device_timeline.create_time` for `rule`'s observable output, `blockchain.create_time` for mined blocks) rather than any service's own `/health` endpoint - those are point-in-time snapshots (uptime, CPU, last-message-age), not a windowed throughput count. New `common/qapu_common/domain/pipeline.py` (`Pipeline_Stats.get_stats(minutes)`, four plain `SELECT count(*) ... WHERE <time column> >= now() - interval` queries, no caching - these are meant to be live) + `GET /hermes/pipeline/stats` (`services/api/src/routers/hermes.py`, `minutes` query param, 1 to 10080/7 days). **`Rule_Events` deliberately does NOT count rule evaluations** - `rule` evaluates every single stream message 1:1, the same count `Data_Processed` already reports, so repeating it would be redundant; it counts `device_timeline` rows instead, rule's actual observable output (a trigger/reset transition), not every evaluation pass. The CLI prints a bordered summary table plus a soft backlog hint (yellow note, not an error) if `Data_Processed` looks meaningfully behind `Ingest_Valid` (`<80%`) - a sign of consumer lag worth checking, not itself proof of one. Live-tested against real prod DB: `--minutes 60` correctly returned all zeros (no test traffic in the preceding hour at that point in the session), `--minutes 1440` correctly returned real 24h counts, and a fresh real packet sent immediately before a `--minutes 5` check showed up as exactly `+1` across Ingest/Data_Processed/Blockchain_Mined (Rule_Events stayed `0`, correctly - that packet's only variable, `PCB_T`, didn't cross any rule threshold).
|
|
358
|
+
|
|
110
359
|
`qapu trend` shows a per-variable history plus a simple linear-regression trend line (slope + direction). Three modes, picked by which options you pass:
|
|
111
360
|
- **Raw readings** (`--last N`, default `N=20`): the measurement cache's rolling buffer - bounded to its own depth (~50 most recent packets), *not* a time window. Good for "what's it doing right now."
|
|
112
361
|
- **Daily aggregates** (`--days N`): one row per calendar day (min/avg/max/count), from the same cache the `/measurement/{device_id}/history` endpoint reads - this is what actually covers multi-day/month windows, since the raw buffer doesn't go back that far. `--days` wins over `--last` if both are given (and `--start`/`--end` isn't).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "qapu-cli"
|
|
3
|
-
version = "0.7.
|
|
3
|
+
version = "0.7.2"
|
|
4
4
|
description = "CLI client for the Qapu API - built for the Hermes agent, but usable by anyone talking to api.ovoo.com.tr from outside the Swarm."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -14,7 +14,9 @@ from typing import Any, Optional
|
|
|
14
14
|
|
|
15
15
|
import httpx
|
|
16
16
|
import typer
|
|
17
|
+
from rich.columns import Columns
|
|
17
18
|
from rich.console import Console
|
|
19
|
+
from rich.panel import Panel
|
|
18
20
|
from rich.table import Table
|
|
19
21
|
from rich.tree import Tree
|
|
20
22
|
|
|
@@ -1054,6 +1056,118 @@ def device_power(
|
|
|
1054
1056
|
_print_metric_groups(device_id, sections, days, as_json)
|
|
1055
1057
|
|
|
1056
1058
|
|
|
1059
|
+
# CT (Current Transformer) Wiring Diagnostic - Added 2026-09-04 per Direct
|
|
1060
|
+
# Field Request. Two Distinct, Real Field Installation Faults, Both
|
|
1061
|
+
# Detectable from PF_R/S/T Alone (no New Raw Variable Needed):
|
|
1062
|
+
#
|
|
1063
|
+
# 1. POLARITY REVERSED (a CT's own IN/OUT terminals swapped) - that
|
|
1064
|
+
# phase's PF Flips SIGN but Keeps a Magnitude Close to the Other
|
|
1065
|
+
# (Correctly-Wired) Phases', Since the Current's Own Waveform is Still
|
|
1066
|
+
# the Right One, Just Measured Backwards. This is Exactly What the
|
|
1067
|
+
# Already-Existing (but Not Auto-Seeded) "Wrong Current Direction"
|
|
1068
|
+
# Rule Group Checks via AE_R/S/T < 0 (`Rule.add_wrong_current_
|
|
1069
|
+
# direction_rule()`, common/qapu_common/domain/rule.py) - PF's Sign is
|
|
1070
|
+
# the Same Signal, Just Available Every Packet Instead of Waiting for
|
|
1071
|
+
# the Cumulative Meter to Actually Go Negative.
|
|
1072
|
+
# 2. PHASE ORDER WRONG (a CT Clamped on the Wrong Line - e.g. R's Current
|
|
1073
|
+
# Actually Flows Through what's Wired as the "S" Channel) - that
|
|
1074
|
+
# Phase's PF Magnitude Itself Looks Wrong (not Just its Sign), Since
|
|
1075
|
+
# Comparing the Wrong Voltage/Current Pair Mixes in the ~120° Phase
|
|
1076
|
+
# Separation Between Lines, on Top of Whatever the Real Load Angle Is.
|
|
1077
|
+
#
|
|
1078
|
+
# Both are Heuristics, not Certainties - Real Confirmation Still Needs a
|
|
1079
|
+
# Physical Check. DEVIATION_THRESHOLD (0.15) Chosen as a Reasonable Rule
|
|
1080
|
+
# of Thumb (a Healthy 3-Phase Motor Load's Phases Normally Track within a
|
|
1081
|
+
# few Percent of Each Other); Not Derived from Real Fault-Case Field Data
|
|
1082
|
+
# (None Available Yet) - Revisit if a Real Miswiring Case Ever Produces a
|
|
1083
|
+
# Reading This Doesn't Correctly Flag.
|
|
1084
|
+
CT_CHECK_DEVIATION_THRESHOLD = 0.15
|
|
1085
|
+
|
|
1086
|
+
|
|
1087
|
+
# Baseline is the MEDIAN of all three phases' |PF|, not an "average of
|
|
1088
|
+
# the other two" (the First Version Tried That and Broke Under a
|
|
1089
|
+
# Multi-Fault Test: One Bad Phase Polluted the Baseline Used to Judge the
|
|
1090
|
+
# *other*, Genuinely Healthy Phases, Flagging Them too - Caught Live
|
|
1091
|
+
# Testing a Deliberate Two-Fault Packet). The Median is Robust to a
|
|
1092
|
+
# Single Outlier - With 3 Phases, One Bad Reading Can Shift the Median
|
|
1093
|
+
# Only If it's the Middle Value, Never Silently Corrupt the Judgment of
|
|
1094
|
+
# the Other Two.
|
|
1095
|
+
def _ct_check_statuses(pf_values: dict[str, Optional[float]]) -> dict[str, tuple[str, str]]:
|
|
1096
|
+
|
|
1097
|
+
magnitudes = sorted(abs(v) for v in pf_values.values() if v is not None)
|
|
1098
|
+
baseline = magnitudes[len(magnitudes) // 2] if magnitudes else None
|
|
1099
|
+
|
|
1100
|
+
results: dict[str, tuple[str, str]] = {}
|
|
1101
|
+
for phase, value in pf_values.items():
|
|
1102
|
+
|
|
1103
|
+
if value is None:
|
|
1104
|
+
results[phase] = ("VERİ YOK", "yellow")
|
|
1105
|
+
continue
|
|
1106
|
+
|
|
1107
|
+
if baseline is None or len(magnitudes) < 2:
|
|
1108
|
+
results[phase] = ("TERS OLABİLİR", "yellow") if value < 0 else ("Normal", "green")
|
|
1109
|
+
continue
|
|
1110
|
+
|
|
1111
|
+
deviation = abs(abs(value) - baseline)
|
|
1112
|
+
if deviation >= CT_CHECK_DEVIATION_THRESHOLD:
|
|
1113
|
+
results[phase] = ("FAZ SIRASI ŞÜPHELİ", "red")
|
|
1114
|
+
elif value < 0:
|
|
1115
|
+
results[phase] = ("TERS BAĞLI (Polarite)", "red")
|
|
1116
|
+
else:
|
|
1117
|
+
results[phase] = ("Normal", "green")
|
|
1118
|
+
|
|
1119
|
+
return results
|
|
1120
|
+
|
|
1121
|
+
|
|
1122
|
+
@app.command("ct-check")
|
|
1123
|
+
def device_ct_check(
|
|
1124
|
+
device_id: str = typer.Argument(..., help="Device ID."),
|
|
1125
|
+
as_json: bool = typer.Option(False, "--json", help="Print raw JSON instead of the visual panel."),
|
|
1126
|
+
):
|
|
1127
|
+
"""Diagnose CT (current transformer) wiring per phase - reversed polarity vs. wrong phase order - from live PF_R/S/T/AE_R/S/T (GET /hermes/data/{device_id})."""
|
|
1128
|
+
readings: list[dict[str, Any]] = client.get(f"/hermes/data/{device_id}")
|
|
1129
|
+
lookup = {r.get("Variable_ID"): r for r in readings}
|
|
1130
|
+
|
|
1131
|
+
pf = {p: lookup.get(f"PF_{p}") for p in ("R", "S", "T")}
|
|
1132
|
+
ae = {p: lookup.get(f"AE_{p}") for p in ("R", "S", "T")}
|
|
1133
|
+
|
|
1134
|
+
if not any(pf.values()):
|
|
1135
|
+
typer.echo("Bu cihaz için PF_R/S/T verisi yok - CT teşhisi yapılamıyor.")
|
|
1136
|
+
return
|
|
1137
|
+
|
|
1138
|
+
pf_values = {p: (float(pf[p]["Value"]) if pf[p] and isinstance(pf[p].get("Value"), (int, float)) else None) for p in ("R", "S", "T")}
|
|
1139
|
+
statuses = _ct_check_statuses(pf_values)
|
|
1140
|
+
|
|
1141
|
+
results = {}
|
|
1142
|
+
for p in ("R", "S", "T"):
|
|
1143
|
+
status, color = statuses[p]
|
|
1144
|
+
results[p] = {
|
|
1145
|
+
"PF": pf[p].get("Value") if pf[p] else None,
|
|
1146
|
+
"AE": ae[p].get("Value") if ae[p] else None,
|
|
1147
|
+
"Time": pf[p].get("Time") if pf[p] else None,
|
|
1148
|
+
"Status": status,
|
|
1149
|
+
"Color": color,
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
if as_json:
|
|
1153
|
+
typer.echo(json.dumps(results, indent=2))
|
|
1154
|
+
return
|
|
1155
|
+
|
|
1156
|
+
console.print(f"[bold]{device_id}[/bold] - CT Kablolama Teşhisi\n")
|
|
1157
|
+
|
|
1158
|
+
panels = []
|
|
1159
|
+
for p in ("R", "S", "T"):
|
|
1160
|
+
r = results[p]
|
|
1161
|
+
pf_str = f"{r['PF']:.4f}" if isinstance(r["PF"], (int, float)) else "-"
|
|
1162
|
+
ae_str = f"{r['AE']:.2f}" if isinstance(r["AE"], (int, float)) else "-"
|
|
1163
|
+
age = _relative_time(r["Time"])
|
|
1164
|
+
body = f"PF: [bold]{pf_str}[/bold]\nAE: {ae_str}\nSon veri: {age}\n\n[bold]{r['Status']}[/bold]"
|
|
1165
|
+
panels.append(Panel(body, title=f"{p} Fazı", border_style=r["Color"], width=24))
|
|
1166
|
+
console.print(Columns(panels))
|
|
1167
|
+
|
|
1168
|
+
console.print("\n[dim]Not: Bu bir sezgisel (heuristic) teşhis, kesin fiziksel doğrulama yerine geçmez - \"FAZ SIRASI ŞÜPHELİ\" veya \"TERS BAĞLI\" çıkan fazı sahada kontrol et.[/dim]")
|
|
1169
|
+
|
|
1170
|
+
|
|
1057
1171
|
# Simple Least-Squares Slope - No Extra Dependency for This, and n < 2
|
|
1058
1172
|
# (Or a Vertical/Degenerate x Range) Just Means "Not Enough Points Yet"
|
|
1059
1173
|
def _linear_slope(xs: list[float], ys: list[float]) -> Optional[float]:
|
|
@@ -1142,6 +1256,53 @@ def device_energy(
|
|
|
1142
1256
|
# Get One Variable's Trend on One Device - Either the Last N Raw Buffered
|
|
1143
1257
|
# Readings, or Daily Min/Avg/Max Over the Last N Days (Mutually Exclusive -
|
|
1144
1258
|
# --days Wins if Both are Given)
|
|
1259
|
+
@app.command("pipeline")
|
|
1260
|
+
def pipeline_stats(
|
|
1261
|
+
minutes: int = typer.Option(60, "--minutes", help="Window size in minutes (default 60, max 10080/7 days)."),
|
|
1262
|
+
as_json: bool = typer.Option(False, "--json", help="Print raw JSON instead of the summary table."),
|
|
1263
|
+
):
|
|
1264
|
+
"""Fleet-wide pipeline throughput for a time window - ingest/data/rule/blockchain packet+event counts (GET /hermes/pipeline/stats)."""
|
|
1265
|
+
result: dict[str, Any] = client.get("/hermes/pipeline/stats", params={"minutes": minutes})
|
|
1266
|
+
|
|
1267
|
+
if as_json:
|
|
1268
|
+
typer.echo(json.dumps(result, indent=2))
|
|
1269
|
+
return
|
|
1270
|
+
|
|
1271
|
+
if not result:
|
|
1272
|
+
typer.echo("İstatistikler alınamadı.")
|
|
1273
|
+
return
|
|
1274
|
+
|
|
1275
|
+
window = result.get("Window_Minutes", minutes)
|
|
1276
|
+
window_label = f"{window} dk" if window < 60 else (f"{window // 60} sa" if window % 60 == 0 else f"{window} dk")
|
|
1277
|
+
|
|
1278
|
+
console.print(f"[bold]Pipeline Özeti[/bold] - son {window_label}\n")
|
|
1279
|
+
|
|
1280
|
+
table = Table(show_header=True, header_style="bold")
|
|
1281
|
+
table.add_column("AŞAMA")
|
|
1282
|
+
table.add_column("SAYI", justify="right")
|
|
1283
|
+
table.add_column("DETAY")
|
|
1284
|
+
|
|
1285
|
+
ingest_total = result.get("Ingest_Total", 0)
|
|
1286
|
+
ingest_valid = result.get("Ingest_Valid", 0)
|
|
1287
|
+
ingest_invalid = result.get("Ingest_Invalid", 0)
|
|
1288
|
+
data_processed = result.get("Data_Processed", 0)
|
|
1289
|
+
rule_events = result.get("Rule_Events", 0)
|
|
1290
|
+
blocks_mined = result.get("Blockchain_Mined", 0)
|
|
1291
|
+
|
|
1292
|
+
table.add_row("Ingest (hardware)", str(ingest_total), f"{ingest_valid} geçerli, {ingest_invalid} geçersiz")
|
|
1293
|
+
table.add_row("Data İşlendi (data)", str(data_processed), "kalibrasyon + sentez tamamlanan paket")
|
|
1294
|
+
table.add_row("Kural Olayları (rule)", str(rule_events), "tetiklenen/sıfırlanan olay (device_timeline)")
|
|
1295
|
+
table.add_row("Blockchain Kazıldı", str(blocks_mined), "yeni blok")
|
|
1296
|
+
|
|
1297
|
+
console.print(table)
|
|
1298
|
+
|
|
1299
|
+
# Simple Backlog Hint - not an Alert, Just a Note if the Numbers Look
|
|
1300
|
+
# Off (data_processed Should Track ingest_valid Closely Since data
|
|
1301
|
+
# Consumes qapu.raw 1:1)
|
|
1302
|
+
if ingest_valid > 0 and data_processed < ingest_valid * 0.8:
|
|
1303
|
+
console.print(f"\n[yellow]Not: Data işlenen paket sayısı ({data_processed}), geçerli ingest sayısından ({ingest_valid}) belirgin şekilde düşük - olası birikme/gecikme, kontrol edilebilir.[/yellow]")
|
|
1304
|
+
|
|
1305
|
+
|
|
1145
1306
|
@app.command("trend")
|
|
1146
1307
|
def device_trend(
|
|
1147
1308
|
device_id: str = typer.Argument(..., help="Device ID."),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qapu-cli
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.2
|
|
4
4
|
Summary: CLI client for the Qapu API - built for the Hermes agent, but usable by anyone talking to api.ovoo.com.tr from outside the Swarm.
|
|
5
5
|
Author: OVOO Technology
|
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -48,6 +48,236 @@ Either way installs a `qapu` command (see `pyproject.toml`'s `[project.scripts]`
|
|
|
48
48
|
|
|
49
49
|
## Commands
|
|
50
50
|
|
|
51
|
+
**Full command reference (100 commands, auto-generated from `main.py`'s docstrings on 2026-09-04 - regenerate the same way if this drifts, don't hand-maintain)**: every command below has `--help` for its full flag list; commands under a resource group (`device`, `variable`, `group`, ...) follow `qapu <group> <command> ...`. The rest of this section gives worked examples and deeper narrative for the commands with more going on (`data`/`trend`/`energy`/`voltage`/`current`/`power`/`ct-check`/`fleet`/`infrastructure`/etc.) - if a command below isn't mentioned again further down, its one-line description here plus `--help` is the whole story, there's nothing more to it.
|
|
52
|
+
|
|
53
|
+
#### Top-level (device-scoped or global)
|
|
54
|
+
|
|
55
|
+
| Command | Description |
|
|
56
|
+
|---|---|
|
|
57
|
+
| `qapu ct-check` | Diagnose CT (current transformer) wiring per phase - reversed polarity vs. wrong phase order - from live PF_R/S/T/AE_R/S/T (GET /hermes/data/{device_id}). |
|
|
58
|
+
| `qapu current` | Current synthesis parameters, grouped (RMS, imbalance, THD, crest factor, fundamental component) (GET /hermes/data/{device_id}). |
|
|
59
|
+
| `qapu data` | Readings per variable for a device - latest value, last N buffered readings (--last), daily min/avg/max (--days), or an explicit --start/--end range (GET /hermes/data/{device_id}). |
|
|
60
|
+
| `qapu energy` | One day's active/reactive energy breakdown per phase (GET /hermes/energy/{device_id}). |
|
|
61
|
+
| `qapu health` | Check CLI/server version, connectivity, and server status (GET /health, no auth required). |
|
|
62
|
+
| `qapu pipeline` | Fleet-wide pipeline throughput for a time window - ingest/data/rule/blockchain packet+event counts (GET /hermes/pipeline/stats). |
|
|
63
|
+
| `qapu power` | Power synthesis parameters, grouped (active/reactive/apparent, imbalance, power factor) (GET /hermes/data/{device_id}). |
|
|
64
|
+
| `qapu timeline` | Get one device's timeline events (GET /hermes/timeline/{device_id}). |
|
|
65
|
+
| `qapu trend` | Trend for one variable on one device: recent readings, daily min/avg/max, or an explicit --start/--end range (GET /hermes/trend/{device_id}/{variable_id}). |
|
|
66
|
+
| `qapu voltage` | Voltage synthesis parameters, grouped (RMS, imbalance, THD, fundamental component) (GET /hermes/data/{device_id}). |
|
|
67
|
+
|
|
68
|
+
#### `device` - device inventory
|
|
69
|
+
|
|
70
|
+
| Command | Description |
|
|
71
|
+
|---|---|
|
|
72
|
+
| `qapu device get` | Get one device's detail (GET /hermes/devices/{device_id}). |
|
|
73
|
+
| `qapu device list` | List every device Qapu knows about (GET /hermes/devices). |
|
|
74
|
+
|
|
75
|
+
#### `variable` - variable catalog
|
|
76
|
+
|
|
77
|
+
| Command | Description |
|
|
78
|
+
|---|---|
|
|
79
|
+
| `qapu variable get` | Get one variable's detail (GET /hermes/variables/{variable_id}). |
|
|
80
|
+
| `qapu variable list` | List the variable catalog (GET /hermes/variables). |
|
|
81
|
+
|
|
82
|
+
#### `group` - device groups
|
|
83
|
+
|
|
84
|
+
| Command | Description |
|
|
85
|
+
|---|---|
|
|
86
|
+
| `qapu group add` | Create a new device group (POST /hermes/groups). |
|
|
87
|
+
| `qapu group assign` | Assign a device to a group (POST /hermes/groups/{group_id}/devices/{device_id}). |
|
|
88
|
+
| `qapu group devices` | List the device IDs assigned to one group (GET /hermes/groups/{group_id}/devices). |
|
|
89
|
+
| `qapu group get` | Get one group's detail (GET /hermes/groups/{group_id}). |
|
|
90
|
+
| `qapu group list` | List device groups (GET /hermes/groups). |
|
|
91
|
+
| `qapu group unassign` | Remove a device's assignment from a group (DELETE /hermes/groups/{group_id}/devices/{device_id}). |
|
|
92
|
+
| `qapu group update` | Update an existing group - only the fields passed are changed (PUT /hermes/groups/{group_id}). |
|
|
93
|
+
|
|
94
|
+
#### `manufacturer` - manufacturer catalog
|
|
95
|
+
|
|
96
|
+
| Command | Description |
|
|
97
|
+
|---|---|
|
|
98
|
+
| `qapu manufacturer add` | Create a new manufacturer (POST /hermes/manufacturers). |
|
|
99
|
+
| `qapu manufacturer delete` | Delete an existing manufacturer (DELETE /hermes/manufacturers/{manufacturer_id}). |
|
|
100
|
+
| `qapu manufacturer get` | Get one manufacturer's detail (GET /hermes/manufacturers/{manufacturer_id}). |
|
|
101
|
+
| `qapu manufacturer list` | List the manufacturer catalog, grouped by category (GET /hermes/manufacturers). |
|
|
102
|
+
| `qapu manufacturer update` | Update an existing manufacturer - only the fields passed are changed (PUT /hermes/manufacturers/{manufacturer_id}). |
|
|
103
|
+
|
|
104
|
+
#### `model` - hardware model catalog
|
|
105
|
+
|
|
106
|
+
| Command | Description |
|
|
107
|
+
|---|---|
|
|
108
|
+
| `qapu model add` | Create a new hardware model, linked to its manufacturer (POST /hermes/models). |
|
|
109
|
+
| `qapu model delete` | Delete an existing hardware model (DELETE /hermes/models/{model_id}). |
|
|
110
|
+
| `qapu model get` | Get one hardware model's detail (GET /hermes/models/{model_id}). |
|
|
111
|
+
| `qapu model list` | List the hardware model catalog, grouped by the same thousands-as-category convention as manufacturers (GET /hermes/models). |
|
|
112
|
+
| `qapu model update` | Update an existing hardware model - only the fields passed are changed (PUT /hermes/models/{model_id}). |
|
|
113
|
+
|
|
114
|
+
#### `modem` - modem inventory
|
|
115
|
+
|
|
116
|
+
| Command | Description |
|
|
117
|
+
|---|---|
|
|
118
|
+
| `qapu modem get` | Get one modem's detail by IMEI (GET /hermes/modems/{imei}). |
|
|
119
|
+
| `qapu modem list` | List the modem inventory (GET /hermes/modems). |
|
|
120
|
+
|
|
121
|
+
#### `sim` - SIM inventory
|
|
122
|
+
|
|
123
|
+
| Command | Description |
|
|
124
|
+
|---|---|
|
|
125
|
+
| `qapu sim get` | Get one SIM's detail by ICCID (GET /hermes/sims/{iccid}). |
|
|
126
|
+
| `qapu sim list` | List the SIM inventory (GET /hermes/sims). |
|
|
127
|
+
|
|
128
|
+
#### `firmware` - firmware catalog
|
|
129
|
+
|
|
130
|
+
| Command | Description |
|
|
131
|
+
|---|---|
|
|
132
|
+
| `qapu firmware get` | Get one firmware version's detail (GET /hermes/firmware/{version}). |
|
|
133
|
+
| `qapu firmware list` | List the firmware catalog (GET /hermes/firmware). |
|
|
134
|
+
|
|
135
|
+
#### `status` - device/modem status catalog
|
|
136
|
+
|
|
137
|
+
| Command | Description |
|
|
138
|
+
|---|---|
|
|
139
|
+
| `qapu status add` | Create a new status (POST /hermes/statuses). |
|
|
140
|
+
| `qapu status delete` | Delete an existing status (DELETE /hermes/statuses/{status_id}). |
|
|
141
|
+
| `qapu status get` | Get one status's detail (GET /hermes/statuses/{status_id}). |
|
|
142
|
+
| `qapu status list` | List the device/modem status catalog, grouped by category (GET /hermes/statuses). |
|
|
143
|
+
| `qapu status update` | Update an existing status - only the fields passed are changed (PUT /hermes/statuses/{status_id}). |
|
|
144
|
+
|
|
145
|
+
#### `equipment-type` - box equipment type catalog
|
|
146
|
+
|
|
147
|
+
| Command | Description |
|
|
148
|
+
|---|---|
|
|
149
|
+
| `qapu equipment-type add` | Create a new equipment type - use before recording box equipment of a kind that doesn't exist in the catalog yet (POST /hermes/equipment_types). |
|
|
150
|
+
| `qapu equipment-type get` | Get one equipment type's detail (GET /hermes/equipment_types/{id}). |
|
|
151
|
+
| `qapu equipment-type list` | List the box equipment type catalog (GET /hermes/equipment_types). |
|
|
152
|
+
|
|
153
|
+
#### `pump-type` - pump type catalog
|
|
154
|
+
|
|
155
|
+
| Command | Description |
|
|
156
|
+
|---|---|
|
|
157
|
+
| `qapu pump-type add` | Create a new pump type - use before recording a pump of a kind that doesn't exist in the catalog yet (POST /hermes/pump_types). |
|
|
158
|
+
| `qapu pump-type delete` | Delete an existing pump type (DELETE /hermes/pump_types/{pump_type_id}). |
|
|
159
|
+
| `qapu pump-type get` | Get one pump type's detail (GET /hermes/pump_types/{id}). |
|
|
160
|
+
| `qapu pump-type list` | List the pump type catalog (GET /hermes/pump_types). |
|
|
161
|
+
| `qapu pump-type update` | Update an existing pump type - only the fields passed are changed (PUT /hermes/pump_types/{pump_type_id}). |
|
|
162
|
+
|
|
163
|
+
#### `infrastructure` - transformer/box/pump/equipment/land plot
|
|
164
|
+
|
|
165
|
+
| Command | Description |
|
|
166
|
+
|---|---|
|
|
167
|
+
| `qapu infrastructure add` | Create standalone infrastructure (transformer/electric box/equipment/pump), or add a land plot to a device's pump. Pick exactly one of --transformer/--electric-box/--equipment/--pump/--land-plot. |
|
|
168
|
+
| `qapu infrastructure assign` | Assign a device to control an existing pump, or monitor a transformer directly. Pick exactly one of --pump or --transformer. |
|
|
169
|
+
| `qapu infrastructure delete` | Delete one piece of box equipment (--equipment), or unlink/delete a device's whole infrastructure chain (DELETE /hermes/infrastructure/...). |
|
|
170
|
+
| `qapu infrastructure get` | Get one device's infrastructure - pump/box/transformer/equipment/land plots, or a direct transformer link (GET /hermes/infrastructure/{device_id}). |
|
|
171
|
+
| `qapu infrastructure update` | Update detail fields on a device's infrastructure. Pick exactly one of --transformer/--electric-box/--equipment/--pump/--land-plot. |
|
|
172
|
+
|
|
173
|
+
#### `blockchain` - mined block ledger
|
|
174
|
+
|
|
175
|
+
| Command | Description |
|
|
176
|
+
|---|---|
|
|
177
|
+
| `qapu blockchain get` | Get one device's block at a given index, full payload (GET /hermes/blockchain/{device_id}/{index}). |
|
|
178
|
+
| `qapu blockchain list` | List every mined block for a device, genesis to latest (GET /hermes/blockchain/{device_id}). |
|
|
179
|
+
| `qapu blockchain validate` | Validate a device's mined block chain - length, secure/tampered status, and time range (GET /hermes/blockchain/{device_id}/validate). |
|
|
180
|
+
|
|
181
|
+
#### `calibration` - Gain/Offset per variable
|
|
182
|
+
|
|
183
|
+
| Command | Description |
|
|
184
|
+
|---|---|
|
|
185
|
+
| `qapu calibration add` | Create a new calibration row for a device/variable (POST /hermes/calibration). |
|
|
186
|
+
| `qapu calibration delete` | Delete an existing calibration row (DELETE /hermes/calibration/{calibration_id}). |
|
|
187
|
+
| `qapu calibration get` | Get one calibration row's detail (GET /hermes/calibration/detail/{calibration_id}). |
|
|
188
|
+
| `qapu calibration list` | List a device's calibration rows - Gain/Offset per variable (GET /hermes/calibration/{device_id}). |
|
|
189
|
+
| `qapu calibration update` | Update an existing calibration row - only the fields passed are changed (PUT /hermes/calibration/{calibration_id}). |
|
|
190
|
+
|
|
191
|
+
#### `crop-type` - crop type catalog
|
|
192
|
+
|
|
193
|
+
| Command | Description |
|
|
194
|
+
|---|---|
|
|
195
|
+
| `qapu crop-type add` | Create a new crop type - use before recording a land plot growing a crop that doesn't exist in the catalog yet (POST /hermes/crop_types). |
|
|
196
|
+
| `qapu crop-type delete` | Delete an existing crop type (DELETE /hermes/crop_types/{crop_type_id}). |
|
|
197
|
+
| `qapu crop-type get` | Get one crop type's detail (GET /hermes/crop_types/{id}). |
|
|
198
|
+
| `qapu crop-type list` | List the crop type catalog (GET /hermes/crop_types). |
|
|
199
|
+
| `qapu crop-type update` | Update an existing crop type - only the fields passed are changed (PUT /hermes/crop_types/{crop_type_id}). |
|
|
200
|
+
|
|
201
|
+
#### `irrigation-type` - irrigation method catalog
|
|
202
|
+
|
|
203
|
+
| Command | Description |
|
|
204
|
+
|---|---|
|
|
205
|
+
| `qapu irrigation-type add` | Create a new irrigation type - use before recording a land plot using a method that doesn't exist in the catalog yet (POST /hermes/irrigation_types). |
|
|
206
|
+
| `qapu irrigation-type delete` | Delete an existing irrigation type (DELETE /hermes/irrigation_types/{irrigation_type_id}). |
|
|
207
|
+
| `qapu irrigation-type get` | Get one irrigation type's detail (GET /hermes/irrigation_types/{id}). |
|
|
208
|
+
| `qapu irrigation-type list` | List the irrigation type catalog (GET /hermes/irrigation_types). |
|
|
209
|
+
| `qapu irrigation-type update` | Update an existing irrigation type - only the fields passed are changed (PUT /hermes/irrigation_types/{irrigation_type_id}). |
|
|
210
|
+
|
|
211
|
+
#### `setting` - rule thresholds, register, electric box
|
|
212
|
+
|
|
213
|
+
| Command | Description |
|
|
214
|
+
|---|---|
|
|
215
|
+
| `qapu setting get` | Get a device's editable settings - thresholds, register, electric box (GET /hermes/setting/{device_id}). |
|
|
216
|
+
| `qapu setting update` | Update a device's editable settings - only the fields passed are changed (PUT /hermes/setting/{device_id}). |
|
|
217
|
+
|
|
218
|
+
#### `stream` - ingested packet history
|
|
219
|
+
|
|
220
|
+
| Command | Description |
|
|
221
|
+
|---|---|
|
|
222
|
+
| `qapu stream get` | Get one stream row's detail - command name and variable count included (GET /hermes/stream/detail/{stream_id}). |
|
|
223
|
+
| `qapu stream list` | List a device's ingested packet stream history, most recent first (GET /hermes/stream/{device_id}). |
|
|
224
|
+
|
|
225
|
+
#### `synthesis` - derived-metric calculation rules
|
|
226
|
+
|
|
227
|
+
| Command | Description |
|
|
228
|
+
|---|---|
|
|
229
|
+
| `qapu synthesis add` | Create a new synthesis rule (POST /hermes/synthesis). |
|
|
230
|
+
| `qapu synthesis delete` | Delete an existing synthesis rule (DELETE /hermes/synthesis/{rule_id}). |
|
|
231
|
+
| `qapu synthesis get` | Get one synthesis rule's detail (GET /hermes/synthesis/{rule_id}). |
|
|
232
|
+
| `qapu synthesis list` | List derived-metric calculation rules (GET /hermes/synthesis). |
|
|
233
|
+
| `qapu synthesis update` | Update an existing synthesis rule - only the fields passed are changed (PUT /hermes/synthesis/{rule_id}). |
|
|
234
|
+
|
|
235
|
+
#### `user-role` - user role catalog (Admin/User/...)
|
|
236
|
+
|
|
237
|
+
| Command | Description |
|
|
238
|
+
|---|---|
|
|
239
|
+
| `qapu user-role add` | Create a new user role (POST /hermes/user_roles). |
|
|
240
|
+
| `qapu user-role delete` | Delete an existing user role (DELETE /hermes/user_roles/{role_id}). |
|
|
241
|
+
| `qapu user-role get` | Get one user role's detail (GET /hermes/user_roles/{role_id}). |
|
|
242
|
+
| `qapu user-role list` | List the user role catalog, e.g. Admin/User (GET /hermes/user_roles). |
|
|
243
|
+
| `qapu user-role update` | Update an existing user role - only the fields passed are changed (PUT /hermes/user_roles/{role_id}). |
|
|
244
|
+
|
|
245
|
+
#### `irrigation` - irrigation event history
|
|
246
|
+
|
|
247
|
+
| Command | Description |
|
|
248
|
+
|---|---|
|
|
249
|
+
| `qapu irrigation abstract` | Get a device's irrigation summary - last irrigation + 24h/today/30d/month totals (GET /hermes/irrigation/{device_id}/abstract). |
|
|
250
|
+
| `qapu irrigation list` | List a device's irrigation event history - start/end/duration (GET /hermes/irrigation/{device_id}). |
|
|
251
|
+
|
|
252
|
+
#### `project` - project catalog (Cinga, WeatherStat, ...)
|
|
253
|
+
|
|
254
|
+
| Command | Description |
|
|
255
|
+
|---|---|
|
|
256
|
+
| `qapu project add` | Create a new project (POST /hermes/projects). |
|
|
257
|
+
| `qapu project delete` | Delete an existing project (DELETE /hermes/projects/{project_id}). |
|
|
258
|
+
| `qapu project devices` | List every device belonging to one project, full detail (GET /hermes/projects/{project_id}/devices). |
|
|
259
|
+
| `qapu project get` | Get one project's detail (GET /hermes/projects/{project_id}). |
|
|
260
|
+
| `qapu project list` | List the project catalog (GET /hermes/projects). |
|
|
261
|
+
| `qapu project update` | Update an existing project - only the fields passed are changed (PUT /hermes/projects/{project_id}). |
|
|
262
|
+
|
|
263
|
+
#### `fleet` - bulk, multi-device queries
|
|
264
|
+
|
|
265
|
+
| Command | Description |
|
|
266
|
+
|---|---|
|
|
267
|
+
| `qapu fleet data` | Latest reading per variable, across every device in a group or project (GET /hermes/data/fleet). |
|
|
268
|
+
|
|
269
|
+
#### `device-command` - command template catalog (not actuation)
|
|
270
|
+
|
|
271
|
+
| Command | Description |
|
|
272
|
+
|---|---|
|
|
273
|
+
| `qapu device-command add` | Create a new device command template (POST /hermes/device_commands). |
|
|
274
|
+
| `qapu device-command delete` | Delete an existing device command template (DELETE /hermes/device_commands/{command_id}). |
|
|
275
|
+
| `qapu device-command get` | Get one device command template's detail, including its full payload Template (GET /hermes/device_commands/{id}). |
|
|
276
|
+
| `qapu device-command list` | List the device command template catalog (GET /hermes/device_commands). |
|
|
277
|
+
| `qapu device-command update` | Update an existing device command template - only the fields passed are changed (PUT /hermes/device_commands/{command_id}). |
|
|
278
|
+
|
|
279
|
+
### Worked examples and deeper detail
|
|
280
|
+
|
|
51
281
|
```bash
|
|
52
282
|
qapu --version # print the installed qapu-cli version and exit
|
|
53
283
|
qapu health # GET /health - no auth, connectivity + CLI/server version + PyPI update check
|
|
@@ -119,6 +349,25 @@ qapu power <device_id> # active/reactive/apparent power, imbalance,
|
|
|
119
349
|
```
|
|
120
350
|
Unlike `qapu energy`, these are **not** daily-by-default - voltage/current/power are instantaneous quantities, not cumulative meters, so there's no natural "daily total" the way energy has one (confirmed with the user before building: "zaman seçimsel olsun... saatlik günlük haftalık" was the ask, but a real hourly bucket doesn't exist anywhere in the cache today, only daily - so `--days N` reuses the exact same vocabulary `data`/`trend` already use rather than inventing a new period concept). Pure CLI-side grouping over the existing `/hermes/data/{device_id}` response - zero new backend logic, each command is a hardcoded `(section title, [Variable_ID, ...])` list in `tools/cli/qapu_cli/main.py` (`VOLTAGE_SECTIONS_COMPACT`/`_FULL_EXTRA`, same pattern for `CURRENT_`/`POWER_`), a section is silently skipped if none of its variables are in the device's reading list. Live-tested against real prod data: a single packet covering the full Voltage+Current+Power raw variable set sent to a disposable test device, all three commands' compact and `--full`/`--json`/`--days` modes confirmed rendering correctly, plus the device-not-found (404) path.
|
|
121
351
|
|
|
352
|
+
**`qapu ct-check` (added 2026-09-04)** - field CT (current transformer) wiring diagnostic, per direct field request ("cihazımız üzerinde 3 adet akım trafosu girişi var... akım trafosu yönleri gibi durumlar oluyor... bunları analiz etmek için bir mekanizma kurmalıyız"):
|
|
353
|
+
```bash
|
|
354
|
+
qapu ct-check <device_id> # visual panel, one box per phase (R/S/T), colored red if flagged
|
|
355
|
+
qapu ct-check <device_id> --json
|
|
356
|
+
```
|
|
357
|
+
Two distinct real installation faults, both diagnosable from `PF_R/S/T` alone (`GET /hermes/data/{device_id}`, no new backend endpoint):
|
|
358
|
+
- **Reversed polarity** (a CT's own IN/OUT terminals swapped) - that phase's PF flips *sign* but keeps a magnitude close to the other phases' - the same signal the already-existing (not auto-seeded) `Rule.add_wrong_current_direction_rule()` checks via `AE_R/S/T < 0`, just available every packet instead of waiting for the cumulative meter to go negative.
|
|
359
|
+
- **Wrong phase order** (a CT clamped on the wrong line, e.g. R's current physically flowing through what's wired as the "S" channel) - that phase's PF *magnitude* itself looks wrong, since comparing the wrong voltage/current pair mixes in the ~120° phase separation between lines on top of the real load angle.
|
|
360
|
+
|
|
361
|
+
Both are heuristics, not certainties - flags a phase for physical inspection, doesn't replace it. Detection: baseline = **median** of all three phases' `|PF|` (not "average of the other two" - the first version tried that and broke under a real multi-fault test, since one bad phase polluted the baseline used to judge the *other*, genuinely healthy phases; the median is robust to a single outlier). `deviation = |own |PF|| - baseline|` - `>= 0.15` (`CT_CHECK_DEVIATION_THRESHOLD`) means "FAZ SIRASI ŞÜPHELİ", else a negative sign means "TERS BAĞLI (Polarite)", else "Normal". The 0.15 threshold is a reasonable rule of thumb (a healthy 3-phase load's phases normally track within a few percent of each other), not derived from real fault-case field data - revisit if a real miswiring case doesn't get flagged correctly. Each phase's panel also shows `AE` (cross-referencing the existing rule's own signal) and "son veri: X dk/saat önce" (via the same `_relative_time()` helper used elsewhere). Live-tested against real prod data via deliberately-crafted `PF_R/S/T` packets sent to a disposable test device: healthy (all three ≈0.93-0.94) → all Normal; single reversed-polarity phase → only that phase flagged, others stay Normal; single wrong-phase-order phase → only that phase flagged; **two simultaneous faults** (one reversed + one phase-order, the case that broke the first "average of other two" version) → all three phases correctly classified independently. Device left in a healthy state afterward.
|
|
362
|
+
|
|
363
|
+
**`qapu pipeline` (added 2026-09-04)** - fleet-wide pipeline throughput for a time window, per direct request ("son x süre içerisinde ingeste kaç paket gelmiş data ne kadar işlemiş rule ne işlemiş gibi... docker servis özetleri"):
|
|
364
|
+
```bash
|
|
365
|
+
qapu pipeline # last 60 minutes (default)
|
|
366
|
+
qapu pipeline --minutes 1440 # last 24 hours
|
|
367
|
+
qapu pipeline --json
|
|
368
|
+
```
|
|
369
|
+
Not device-scoped - a fleet-wide summary across every service in the `ingest -> stream -> calibration -> raw-writer -> synthesis -> ... -> rule` pipeline. Deliberately reads from the real tables each stage actually writes to (`raw_data.stream_time`/`valid_pack` for `hardware`'s ingest, `streams.stream_time` for `data`'s calibration+synthesis completion, `device_timeline.create_time` for `rule`'s observable output, `blockchain.create_time` for mined blocks) rather than any service's own `/health` endpoint - those are point-in-time snapshots (uptime, CPU, last-message-age), not a windowed throughput count. New `common/qapu_common/domain/pipeline.py` (`Pipeline_Stats.get_stats(minutes)`, four plain `SELECT count(*) ... WHERE <time column> >= now() - interval` queries, no caching - these are meant to be live) + `GET /hermes/pipeline/stats` (`services/api/src/routers/hermes.py`, `minutes` query param, 1 to 10080/7 days). **`Rule_Events` deliberately does NOT count rule evaluations** - `rule` evaluates every single stream message 1:1, the same count `Data_Processed` already reports, so repeating it would be redundant; it counts `device_timeline` rows instead, rule's actual observable output (a trigger/reset transition), not every evaluation pass. The CLI prints a bordered summary table plus a soft backlog hint (yellow note, not an error) if `Data_Processed` looks meaningfully behind `Ingest_Valid` (`<80%`) - a sign of consumer lag worth checking, not itself proof of one. Live-tested against real prod DB: `--minutes 60` correctly returned all zeros (no test traffic in the preceding hour at that point in the session), `--minutes 1440` correctly returned real 24h counts, and a fresh real packet sent immediately before a `--minutes 5` check showed up as exactly `+1` across Ingest/Data_Processed/Blockchain_Mined (Rule_Events stayed `0`, correctly - that packet's only variable, `PCB_T`, didn't cross any rule threshold).
|
|
370
|
+
|
|
122
371
|
`qapu trend` shows a per-variable history plus a simple linear-regression trend line (slope + direction). Three modes, picked by which options you pass:
|
|
123
372
|
- **Raw readings** (`--last N`, default `N=20`): the measurement cache's rolling buffer - bounded to its own depth (~50 most recent packets), *not* a time window. Good for "what's it doing right now."
|
|
124
373
|
- **Daily aggregates** (`--days N`): one row per calendar day (min/avg/max/count), from the same cache the `/measurement/{device_id}/history` endpoint reads - this is what actually covers multi-day/month windows, since the raw buffer doesn't go back that far. `--days` wins over `--last` if both are given (and `--start`/`--end` isn't).
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|