|
|
|
@@ -0,0 +1,515 @@
|
|
|
|
|
# Machine-readable notes for this export
|
|
|
|
|
|
|
|
|
|
Companion to `README.md`, which says the same things in plain language for the
|
|
|
|
|
person this data belongs to. This file is the precise version: complete reference
|
|
|
|
|
tables, exact types, and the places where a careless reading goes wrong.
|
|
|
|
|
|
|
|
|
|
If you are an assistant helping someone read this folder, read this file first.
|
|
|
|
|
|
|
|
|
|
## Three things that will make you wrong
|
|
|
|
|
|
|
|
|
|
**1. `p` in the event log is not self-describing.**
|
|
|
|
|
It is a positional JSON array. The key order is *not* in the data — it is in the
|
|
|
|
|
per-code tables below. Do not guess field names from the values.
|
|
|
|
|
|
|
|
|
|
**2. `e` and `event_id` are unrelated numbering schemes.**
|
|
|
|
|
`e` in the event log says *what happened*. `event_id` in the statistics says *what is
|
|
|
|
|
being measured*. They collide and mean different things:
|
|
|
|
|
|
|
|
|
|
| Number | `e` (event log) | `event_id` (statistics) |
|
|
|
|
|
| --- | --- | --- |
|
|
|
|
|
| 1 | Switched on or off | Switch or relay with energy metering |
|
|
|
|
|
| 4 | A battery-powered device woke up and reported | Temperature and humidity sensor |
|
|
|
|
|
| 8 | Brightness changed | Door, window or position sensor |
|
|
|
|
|
| 12 | Smoke alarm | Temperature sensor |
|
|
|
|
|
| 13 | An input (switch or button) changed | Gas or air-quality sensor |
|
|
|
|
|
| 16 | A Bluetooth temperature and humidity reading | Analogue input |
|
|
|
|
|
|
|
|
|
|
Never resolve one against the other.
|
|
|
|
|
|
|
|
|
|
**3. Absence is not nonexistence.** Whole categories were removed from this export by
|
|
|
|
|
policy, listed under "Excluded" below. If something is not here, that says nothing
|
|
|
|
|
about whether the account used it. Do not conclude the owner had no automations.
|
|
|
|
|
|
|
|
|
|
## Identity
|
|
|
|
|
|
|
|
|
|
One physical device, written three ways:
|
|
|
|
|
|
|
|
|
|
| Form | Value | Appears in |
|
|
|
|
|
| --- | --- | --- |
|
|
|
|
|
| decimal | `XLQd6aExkzDOkdyDoV0KQer` | statistics `device_id` |
|
|
|
|
|
| hex | `XLQd6aExkzDOkdyDoV0KQer` | `device.json` id of channel 0, event `tag` |
|
|
|
|
|
| hex + channel | `XLQd6aExkzDOkdyDoV0KQer_<n>` | `device.json` ids, event `tag` |
|
|
|
|
|
|
|
|
|
|
Conversion: `parseInt("XLQd6aExkzDOkdyDoV0KQer", 16) === XLQd6aExkzDOkdyDoV0KQer`. Device ids beginning
|
|
|
|
|
with `X` are not numeric and have no hex form; they appear verbatim everywhere.
|
|
|
|
|
|
|
|
|
|
**Channels.** 1 channel(s). The bare hex id is channel 0;
|
|
|
|
|
`<hex>_N` is channel N. Each is a separate record with its own name, room and image,
|
|
|
|
|
but all are one physical unit.
|
|
|
|
|
|
|
|
|
|
| id | channel | name |
|
|
|
|
|
| --- | --- | --- |
|
|
|
|
|
| `XLQd6aExkzDOkdyDoV0KQer` | 0 | Back door |
|
|
|
|
|
|
|
|
|
|
**Rooms.** `room_id` occurs in `device.json` and in the statistics as a bare
|
|
|
|
|
integer. Only the ids this bundle's own records use are resolved here; the home's
|
|
|
|
|
full room list is out of scope:
|
|
|
|
|
|
|
|
|
|
| `room_id` | name |
|
|
|
|
|
| --- | --- |
|
|
|
|
|
| 2 | Lab |
|
|
|
|
|
|
|
|
|
|
`room_id` records the assignment **at write time**, so one channel can carry several
|
|
|
|
|
across its history and a row does not tell you where the device is now. An id with
|
|
|
|
|
no name could not be resolved against the home's current rooms - it may be deleted,
|
|
|
|
|
or a sentinel written before assignment (`-10` occurs on a device's first day).
|
|
|
|
|
**Do not infer which.** It is listed rather than dropped so that no integer in the
|
|
|
|
|
data is left unexplained, and the same applies to `tariff_id`: a negative value
|
|
|
|
|
there is a placeholder, not a tariff you can look up.
|
|
|
|
|
|
|
|
|
|
## Joining the files
|
|
|
|
|
|
|
|
|
|
`event_log.tag` → channel number → `statistics.channel`:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
XLQd6aExkzDOkdyDoV0KQer -> channel 0
|
|
|
|
|
XLQd6aExkzDOkdyDoV0KQer_N -> channel N
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**A suffix is dependable; the absence of one is not.** Across seven houses and
|
|
|
|
|
~120,000 events a `_N` suffix never disagreed with the payload channel. That is an
|
|
|
|
|
observation, not a guarantee — treat it as a strong default, not an invariant to
|
|
|
|
|
assert on. A bare tag usually means channel 0 but sometimes does not —
|
|
|
|
|
Cury devices (event code 21) keep a bare tag and put the channel only in the
|
|
|
|
|
payload, and log entries written before roughly 19 December 2022 do the same for
|
|
|
|
|
other codes, from before the suffix existed.
|
|
|
|
|
|
|
|
|
|
So: **where the payload carries a channel, prefer it over the tag.** Fall back to the
|
|
|
|
|
tag suffix only for codes whose payload has no channel field. Getting this backwards
|
|
|
|
|
silently files one channel's history under another.
|
|
|
|
|
|
|
|
|
|
Payload position 1 is the channel for codes 1, 2, 5, 8, 11, 12, 13 and 21 only. For
|
|
|
|
|
every other code that position is something else entirely — `reason` for code 4,
|
|
|
|
|
`scnid` for code 6 — so do not read it as a channel there.
|
|
|
|
|
|
|
|
|
|
**A component reference is not a number, but it does join.** Add-on and virtual
|
|
|
|
|
components report `channel` as a reference — `enum:200`, `boolean:201`, `number:200`
|
|
|
|
|
— mostly under codes 17 and 22, and their tag carries no `_N` suffix. Coercing the
|
|
|
|
|
reference itself to an integer produces nonsense, but **the numeric part after the
|
|
|
|
|
colon is the statistics `channel`**: `number:200` joins to `channel` `200`. Split on
|
|
|
|
|
the colon rather than discarding the row. Confirmed against a real export.
|
|
|
|
|
|
|
|
|
|
Times do not join directly. Event `t` is an integer of **milliseconds since the Unix
|
|
|
|
|
epoch**; statistics `_time` is an **ISO 8601 string in UTC** marking the *start* of
|
|
|
|
|
the period the row covers. Convert before comparing.
|
|
|
|
|
|
|
|
|
|
## Inventory
|
|
|
|
|
|
|
|
|
|
| File | Rows | Bytes | Selected by |
|
|
|
|
|
| --- | --- | --- | --- |
|
|
|
|
|
| `device.json` | 1 channel(s) | — | hex id and `XLQd6aExkzDOkdyDoV0KQer_<n>` |
|
|
|
|
|
| `event_log.csv` | 4 | — | event `tag` |
|
|
|
|
|
| `last_known_state.json` | 3 key(s) | — | `DEV:XLQd6aExkzDOkdyDoV0KQer:*` |
|
|
|
|
|
| `images/` | 0 file(s) | — | `image` field of each channel |
|
|
|
|
|
|
|
|
|
|
The same inventory, for a tool rather than a reader. This device produced no
|
|
|
|
|
statistics at all, which the empty `files` list states rather than implies:
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"schema": "shelly-device-export/2",
|
|
|
|
|
"event_log": {
|
|
|
|
|
"rows": 4,
|
|
|
|
|
"matched": 4,
|
|
|
|
|
"withheld": 0,
|
|
|
|
|
"cap": 5,
|
|
|
|
|
"cap_scope": "device",
|
|
|
|
|
"per_tag": {
|
|
|
|
|
"XLQd6aExkzDOkdyDoV0KQer": 4
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"files": []
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Schemas
|
|
|
|
|
|
|
|
|
|
### `last_known_state.json`
|
|
|
|
|
|
|
|
|
|
The device's own view of itself, as the platform last cached it. Three sources,
|
|
|
|
|
each a Redis hash under `DEV:<decimal-id>:` in the per-server database that holds
|
|
|
|
|
device state:
|
|
|
|
|
|
|
|
|
|
| Member | Source key | Holds |
|
|
|
|
|
| --- | --- | --- |
|
|
|
|
|
| `info` | `INFO` | identity and connection metadata — `id`, `id16`, `type`, `code`, `gen`, `online`, `conntime`, `ip` |
|
|
|
|
|
| `status` | `STATUS` | last reported component state, one member per component (`switch:0`, `input:1`, `wifi`, …) |
|
|
|
|
|
| `settings` | `SETTINGS` | device configuration as its own firmware reports it |
|
|
|
|
|
|
|
|
|
|
Hash field values are **JSON where they parse and the raw string where they do
|
|
|
|
|
not** — `code` is a bare model string, `wifi` is an object. Parse defensively.
|
|
|
|
|
Which of the three are present, which were not there at all, and which values were
|
|
|
|
|
blanked are all stated as data: `absent` and `redacted` in the file, and the
|
|
|
|
|
Provenance section below.
|
|
|
|
|
|
|
|
|
|
`[redacted]` marks a value removed because its key is secret-shaped. It means the
|
|
|
|
|
field existed and held something; a field that was empty is left as it was and is
|
|
|
|
|
**not** listed in `redacted`, so the list never implies a secret that never was.
|
|
|
|
|
|
|
|
|
|
### `event_log.csv`
|
|
|
|
|
|
|
|
|
|
| Column | Type | Notes |
|
|
|
|
|
| --- | --- | --- |
|
|
|
|
|
| `tag` | string | `XLQd6aExkzDOkdyDoV0KQer` or `XLQd6aExkzDOkdyDoV0KQer_<n>` |
|
|
|
|
|
| `t` | integer | milliseconds since the Unix epoch, UTC |
|
|
|
|
|
| `e` | integer | event code; see the full table below |
|
|
|
|
|
| `p` | JSON string | positional array |
|
|
|
|
|
|
|
|
|
|
Rows: 4 of 4 matched, cap 5. Source table `event_logs` (version 1).
|
|
|
|
|
Range: 2026-10-21T07:10:53.259Z to 2026-10-21T07:22:58.358Z - the rows in this file.
|
|
|
|
|
Codes present: 18.
|
|
|
|
|
That list describes the rows this file carries, which may be a subset of what the
|
|
|
|
|
device produced - see Provenance for the counts. A code absent from it is not
|
|
|
|
|
evidence the device never produced one.
|
|
|
|
|
|
|
|
|
|
## Sentinels and traps
|
|
|
|
|
|
|
|
|
|
- Every timestamp in the data files is UTC - `event_log.csv` `t`.
|
|
|
|
|
It carries no offset, and there is no local-time *timestamp* anywhere.
|
|
|
|
|
`last_known_state.json` is the exception worth knowing about: it carries the
|
|
|
|
|
device's own clock configuration - a zone name, a UTC offset in seconds, and a
|
|
|
|
|
wall-clock string like `"15:50"`. Those describe the device, not the data, and
|
|
|
|
|
must not be used to reinterpret a timestamp that is already UTC.
|
|
|
|
|
- `device.json` `image` values under `images/device_images/` are stock artwork shipped
|
|
|
|
|
with the app, not uploads; only `assets/user_images/...` paths correspond to a file
|
|
|
|
|
in `images/`.
|
|
|
|
|
|
|
|
|
|
## Loading into SQLite
|
|
|
|
|
|
|
|
|
|
The most practical way to work with this, and it keeps the data on the machine it is
|
|
|
|
|
already on. Free GUI clients (DB Browser for SQLite, DBeaver) open the result.
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
sqlite3 device.db
|
|
|
|
|
.mode csv
|
|
|
|
|
.import event_log.csv ev
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**`.import` types every column TEXT.** Numeric work needs an explicit cast, or
|
|
|
|
|
`sum()` silently returns 0:
|
|
|
|
|
|
|
|
|
|
```sql
|
|
|
|
|
-- decode the event log: positional payload and epoch, in one pass
|
|
|
|
|
select datetime(cast(t as integer)/1000, 'unixepoch') as when_utc,
|
|
|
|
|
json_extract(p, '$[0]') as device,
|
|
|
|
|
json_extract(p, '$[1]') as channel,
|
|
|
|
|
json_extract(p, '$[2]') as value
|
|
|
|
|
from ev order by cast(t as integer);
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Data quality: what you may legitimately see
|
|
|
|
|
|
|
|
|
|
This is an export of what is stored, not a cleaned or corrected version. Some of it
|
|
|
|
|
will look wrong, and usually is wrong at source rather than in transit:
|
|
|
|
|
|
|
|
|
|
- **A device can be dead and still attached to the account.** It may keep reporting
|
|
|
|
|
implausible values, or stop reporting while still appearing in `device.json`. A
|
|
|
|
|
gap in the statistics is not evidence the device was off.
|
|
|
|
|
- **Firmware and platform changes alter what a device reports** over the years these
|
|
|
|
|
files cover. A field can change meaning, units, or stop being written, without
|
|
|
|
|
anything in the data marking the change.
|
|
|
|
|
- **Out-of-range readings are common** — a temperature of hundreds of degrees, an
|
|
|
|
|
energy spike, a battery above 100. These are what the device sent.
|
|
|
|
|
|
|
|
|
|
If you are summarising this for someone: **do not silently repair it.** Do not
|
|
|
|
|
interpolate gaps, drop outliers without saying so, or average across a discontinuity.
|
|
|
|
|
Report anomalies as anomalies — an average that quietly includes a nonsense spike is
|
|
|
|
|
worse than no average, and the person reading it cannot tell the difference.
|
|
|
|
|
|
|
|
|
|
## Reference: all event-log codes
|
|
|
|
|
|
|
|
|
|
The complete set the platform defines, independent of what this bundle contains - so
|
|
|
|
|
you can tell a code that is absent from this export from one that does not exist.
|
|
|
|
|
Which codes occur here is stated as data, in `Codes present:` above.
|
|
|
|
|
`keys` is the order of values in a positional payload.
|
|
|
|
|
|
|
|
|
|
**This reference reflects the platform as of 2026-10-23.** The
|
|
|
|
|
services that write this data change. A code may have been added, or a payload
|
|
|
|
|
reshaped, since this export was produced. Where the data disagrees with the table
|
|
|
|
|
below, the data is what we hold — trust it and say the table looks out of date.
|
|
|
|
|
|
|
|
|
|
| `e` | name | meaning | payload order |
|
|
|
|
|
| --- | --- | --- | --- |
|
|
|
|
|
| 1 | `onOff` | Switched on or off | `deviceId`, `channel`, `turn` |
|
|
|
|
|
| 2 | `overPower` | Power limit exceeded | `deviceId`, `channel` |
|
|
|
|
|
| 3 | `overTemperature` | Temperature limit exceeded | `deviceId`, `tmp` |
|
|
|
|
|
| 4 | `wakeUp` | A battery-powered device woke up and reported | `deviceId`, `reason`, `tmp`, `bat`, `alarm_hum` |
|
|
|
|
|
| 5 | `openCloseStop` | A blind or shutter opened, closed or stopped | `deviceId`, `channel`, `state`, `current_pos` |
|
|
|
|
|
| 6 | `sceneExec` | An automation ran | `tstamp`, `scnid` |
|
|
|
|
|
| 7 | `DwState` | A door or window opened or closed | `deviceId`, `reason`, `state`, `bat`, `lux_value`, `lux_illumination`, `tilt`, `vibration`, `tmp` |
|
|
|
|
|
| 8 | `brightness` | Brightness changed | `deviceId`, `channel`, `brightness` |
|
|
|
|
|
| 9 | `gasSensor` | Gas alarm changed | `deviceId`, `alarm_gas` |
|
|
|
|
|
| 10 | `motion` | Motion was detected | `deviceId`, `motion`, `bat`, `lux_value`, `lux_illumination`, `tmp` |
|
|
|
|
|
| 11 | `gen2Errors` | The device reported an error | `deviceId`, `channel`, `error`, `...error-specific values` |
|
|
|
|
|
| 12 | `smokeAlarm` | Smoke alarm | `deviceId`, `channel` |
|
|
|
|
|
| 13 | `inputState` | An input (switch or button) changed | `deviceId`, `channel`, `state`, `component` |
|
|
|
|
|
| 14 | `DwBleState` | A Bluetooth door or window sensor opened or closed | `deviceId`, `reason`, `state`, `bat`, `lux`, `tilt` |
|
|
|
|
|
| 15 | `MotionBleState` | A Bluetooth motion sensor detected motion | `deviceId`, `motion`, `bat`, `lux_value` |
|
|
|
|
|
| 16 | `HnTBLEState` | A Bluetooth temperature and humidity reading | `deviceId`, `tmp`, `hum`, `bat`, `lux_illumination` |
|
|
|
|
|
| 17 | `xModGeneric` | A reading from an add-on or Shelly X module | `deviceId`, `ref`, `value` |
|
|
|
|
|
| 18 | `lockState` | A lock was locked or unlocked | `deviceId`, `state` |
|
|
|
|
|
| 19 | `vibrationDistanceBatState` | A vibration or distance reading | `deviceId`, `state`, `distance`, `bat` |
|
|
|
|
|
| 20 | `presence` | Presence was detected | `deviceId`, `ref`, `state`, `num_objects` |
|
|
|
|
|
| 21 | `cury` | A Cury device changed state | `deviceId`, `channel`, `state`, `level` |
|
|
|
|
|
| 22 | `camera` | A camera detected motion | `deviceId`, `zone`, `motion`, `rec_id`, `media_id` |
|
|
|
|
|
| 23 | `breakerRPC` | A circuit breaker changed state | `deviceId`, `state`, `reason` |
|
|
|
|
|
| 24 | `cameraStatus` | A camera arm or privacy setting changed | `deviceId`, `arm`, `privacy` |
|
|
|
|
|
|
|
|
|
|
Payload key meanings:
|
|
|
|
|
|
|
|
|
|
| Key | Meaning |
|
|
|
|
|
| --- | --- |
|
|
|
|
|
| `deviceId` | the device this event came from |
|
|
|
|
|
| `channel` | which output or channel of the device |
|
|
|
|
|
| `turn` | true = switched on, false = switched off |
|
|
|
|
|
| `tmp` | temperature, in degrees Celsius |
|
|
|
|
|
| `hum` | relative humidity, in percent |
|
|
|
|
|
| `bat` | battery level remaining, in percent |
|
|
|
|
|
| `reason` | what prompted the report |
|
|
|
|
|
| `state` | the new state |
|
|
|
|
|
| `current_pos` | position, in percent |
|
|
|
|
|
| `tstamp` | when it happened, in milliseconds since 1 January 1970 (UTC) |
|
|
|
|
|
| `scnid` | the identifier of the automation that ran |
|
|
|
|
|
| `lux` | light level |
|
|
|
|
|
| `lux_value` | measured light level |
|
|
|
|
|
| `lux_illumination` | light level as a description (for example dark or bright) |
|
|
|
|
|
| `tilt` | tilt angle |
|
|
|
|
|
| `vibration` | whether vibration was detected |
|
|
|
|
|
| `brightness` | brightness, in percent |
|
|
|
|
|
| `alarm_gas` | gas alarm state |
|
|
|
|
|
| `alarm_hum` | humidity alarm state |
|
|
|
|
|
| `motion` | whether motion was detected |
|
|
|
|
|
| `error` | the error the device reported |
|
|
|
|
|
| `component` | which part of the device (for example an input) |
|
|
|
|
|
| `ref` | which sub-component reported |
|
|
|
|
|
| `value` | the reported value |
|
|
|
|
|
| `distance` | measured distance |
|
|
|
|
|
| `num_objects` | how many were detected |
|
|
|
|
|
| `level` | the reported level |
|
|
|
|
|
| `zone` | which camera zone |
|
|
|
|
|
| `rec_id` | recording identifier |
|
|
|
|
|
| `media_id` | media identifier |
|
|
|
|
|
| `arm` | whether the camera was armed |
|
|
|
|
|
| `privacy` | whether privacy mode was on |
|
|
|
|
|
|
|
|
|
|
Caveats worth carrying: the payload of code 4 varies by device model (a Gen2 smoke
|
|
|
|
|
sensor writes `[deviceId, reason, battery]`), and code 11 appends error-specific
|
|
|
|
|
values after `error`.
|
|
|
|
|
|
|
|
|
|
## Reference: measurement families
|
|
|
|
|
|
|
|
|
|
Every family this tool can export, independent of what this bundle contains, so an
|
|
|
|
|
absent `event_id` can be told from an undocumented one. This tool reads statistics at
|
|
|
|
|
the raw, `_1m`, `_1h` and `_1d` resolutions, and from the net metering, monthly and
|
|
|
|
|
archived-meter stores. Which of those appear in a given bundle depends on the
|
|
|
|
|
scope of the export, on what the device wrote, and on the options it ran with -
|
|
|
|
|
the file inventory above and Provenance below are what say. One thing is written by
|
|
|
|
|
the platform but falls outside that scope and is in no bundle:
|
|
|
|
|
|
|
|
|
|
- `overall_consumption` - Whole-platform consumption totals. These carry no tags at all, not even a home id, so no part of them belongs to any one account.
|
|
|
|
|
|
|
|
|
|
**This reference reflects the platform as of 2026-10-23.** Families
|
|
|
|
|
may have been added, or fields reshaped, since this export was produced. Where the
|
|
|
|
|
data disagrees with the table below, the data is what we hold.
|
|
|
|
|
|
|
|
|
|
Families present in this bundle: none.
|
|
|
|
|
|
|
|
|
|
| `event_id` | measurement | fields |
|
|
|
|
|
| --- | --- | --- |
|
|
|
|
|
| 1 | Switch or relay with energy metering | `f1` channel number; `f2` mains voltage, in volts; `f3` energy used during the period, in watt-minutes - divide by 60000 for kwh; `f4` energy returned to the grid during the period, in watt-minutes - divide by 60000 for kwh |
|
|
|
|
|
| 4 | Temperature and humidity sensor | `f2` relative humidity, in percent; `f3` lowest humidity during the period, in percent; `f4` highest humidity during the period, in percent; `f5` lowest temperature during the period, in degrees celsius; `f6` highest temperature during the period, in degrees celsius |
|
|
|
|
|
| 8 | Door, window or position sensor | `f2` position, in percent; `f3` battery level remaining, in percent; `f5` lowest temperature during the period, in degrees celsius; `f6` highest temperature during the period, in degrees celsius |
|
|
|
|
|
| 10 | Energy meter | `f1` channel number; `f3` energy used during the period, in watt-hours - divide by 1000 for kwh; `f4` energy returned to the grid during the period, in watt-hours - divide by 1000 for kwh; `f5` lowest voltage during the period, in volts; `f6` highest voltage during the period, in volts |
|
|
|
|
|
| 12 | Temperature sensor | `f1` channel number; `f3` lowest temperature during the period, in degrees celsius; `f4` highest temperature during the period, in degrees celsius |
|
|
|
|
|
| 13 | Gas or air-quality sensor | `f1` measured concentration; `f2` measured concentration |
|
|
|
|
|
| 14 | Humidity sensor | `f1` channel number; `f3` lowest humidity during the period, in percent; `f4` highest humidity during the period, in percent |
|
|
|
|
|
| 15 | Voltage measurement | `f3` lowest voltage during the period, in volts; `f4` highest voltage during the period, in volts |
|
|
|
|
|
| 16 | Analogue input | `f3` lowest reading during the period, in percent; `f4` highest reading during the period, in percent |
|
|
|
|
|
| 17 | Counter or accumulated total | |
|
|
|
|
|
| 18 | Frequency, presence, soil or other generic sensor | |
|
|
|
|
|
| 19 | Generic counter | `counter` accumulated count |
|
|
|
|
|
| 20 | Weather station | `temperature` temperature, in degrees celsius; `min_temperature` lowest temperature during the period, in degrees celsius; `max_temperature` highest temperature during the period, in degrees celsius; `humidity` relative humidity, in percent; `pressure` air pressure; `min_pressure` lowest air pressure during the period; `max_pressure` highest air pressure during the period; `illuminance` light level, in lux; `precipitation` rainfall during the period; `uv` ultraviolet index; `wind_speed` wind speed |
|
|
|
|
|
| 21 | Air-quality sensor | `co2` carbon dioxide concentration, in ppm; `humidity` relative humidity, in percent; `min_temperature` lowest temperature during the period, in degrees celsius; `max_temperature` highest temperature during the period, in degrees celsius; `min_pressure` lowest air pressure during the period; `max_pressure` highest air pressure during the period |
|
|
|
|
|
|
|
|
|
|
## Reference: machine-readable summary
|
|
|
|
|
|
|
|
|
|
The parts of this document a tool should act on rather than read: the scale factors,
|
|
|
|
|
what makes a statistics row unique, the event log's per-tag cap, and the defects the
|
|
|
|
|
data is known to carry. Every value here is generated from the same constants that
|
|
|
|
|
produced the prose above, so the two cannot disagree. It states no retention window
|
|
|
|
|
for the statistics, deliberately - see *Expired* below.
|
|
|
|
|
|
|
|
|
|
A unit stated in English can be confidently wrong in a way a scale factor cannot -
|
|
|
|
|
these documents described the energy columns in the wrong unit for months, and every
|
|
|
|
|
sentence saying so agreed with every other. `to_kwh` is the number that settles it.
|
|
|
|
|
**Read it per family.** `f3` does not hold the same unit everywhere: a relay writes
|
|
|
|
|
watt-minutes and an energy meter writes watt-hours, which is a factor of 60 between
|
|
|
|
|
two columns of the same name. Match the `families` list against a row's `event_id`
|
|
|
|
|
rather than taking the first entry.
|
|
|
|
|
|
|
|
|
|
This describes the platform, not this bundle: it is identical in every export. The
|
|
|
|
|
measured figures for *this* one are in the Inventory block above.
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
{
|
|
|
|
|
"schema": "shelly-device-export/2",
|
|
|
|
|
"columns": {
|
|
|
|
|
"f3": [
|
|
|
|
|
{
|
|
|
|
|
"families": [
|
|
|
|
|
1
|
|
|
|
|
],
|
|
|
|
|
"unit": "W·min",
|
|
|
|
|
"to_kwh": 0.000016666666666666667
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"families": [
|
|
|
|
|
10
|
|
|
|
|
],
|
|
|
|
|
"unit": "W·h",
|
|
|
|
|
"to_kwh": 0.001
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"f4": [
|
|
|
|
|
{
|
|
|
|
|
"families": [
|
|
|
|
|
1
|
|
|
|
|
],
|
|
|
|
|
"unit": "W·min",
|
|
|
|
|
"to_kwh": 0.000016666666666666667
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"families": [
|
|
|
|
|
10
|
|
|
|
|
],
|
|
|
|
|
"unit": "W·h",
|
|
|
|
|
"to_kwh": 0.001
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"energy_price": {
|
|
|
|
|
"scale_of": "f3",
|
|
|
|
|
"to_account_currency": [
|
|
|
|
|
{
|
|
|
|
|
"families": [
|
|
|
|
|
1
|
|
|
|
|
],
|
|
|
|
|
"factor": 0.000016666666666666667
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"families": [
|
|
|
|
|
10
|
|
|
|
|
],
|
|
|
|
|
"factor": 0.001
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"currency": null,
|
|
|
|
|
"currency_applies_to_rows": false,
|
|
|
|
|
"currency_at_export_in": "provenance"
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"grain": [
|
|
|
|
|
"_time",
|
|
|
|
|
"channel",
|
|
|
|
|
"room_id",
|
|
|
|
|
"tariff_id"
|
|
|
|
|
],
|
|
|
|
|
"event_log_retention_per_tag_upstream": 100,
|
|
|
|
|
"known_gaps": [
|
|
|
|
|
{
|
|
|
|
|
"id": "daily-rollup-missing-2300-utc",
|
|
|
|
|
"summary": "`_1d` rows for days before 2026-09-23 omit the 23:00 UTC hour; the rollup was corrected on that date and rows from it are whole, so adding the hour back to a later row double-counts it.",
|
|
|
|
|
"families": null
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"id": "f3-net-or-gross",
|
|
|
|
|
"summary": "`f3` is net of returned energy on some device families and gross on others, with nothing in the row to say which, so it can be negative.",
|
|
|
|
|
"families": [
|
|
|
|
|
1,
|
|
|
|
|
10
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Excluded — absence here means nothing
|
|
|
|
|
|
|
|
|
|
Five distinct reasons produce a gap in this bundle. **None of them is evidence about
|
|
|
|
|
the owner or the device**, and they are not interchangeable:
|
|
|
|
|
|
|
|
|
|
**1. Out of scope — held by the platform, deliberately not in a device export.**
|
|
|
|
|
|
|
|
|
|
- automations and scenes, including scene-execution events
|
|
|
|
|
- dashboards, groups, room definitions
|
|
|
|
|
- account-level records: identity, contact details, locale and display preferences,
|
|
|
|
|
billing data
|
|
|
|
|
- third-party integrations (for example Google Home sync)
|
|
|
|
|
- account ownership, access rights, server placement
|
|
|
|
|
- whole-house monthly energy reports
|
|
|
|
|
- every other device in the same home
|
|
|
|
|
|
|
|
|
|
This bundle is scoped to one device. It should contain no other device identifier.
|
|
|
|
|
Do not conclude the owner had no automations, or that the home had one device.
|
|
|
|
|
|
|
|
|
|
**2. Expired — deleted by retention, unavailable to anyone.** Each store keeps rows
|
|
|
|
|
for a window and the platform deletes them past it.
|
|
|
|
|
|
|
|
|
|
**The windows are not stated here, and should not be inferred.** They are configured
|
|
|
|
|
per server and are not uniform across the fleet - on at least one host the per-minute
|
|
|
|
|
store is deliberately longer than elsewhere. A figure printed in this document would
|
|
|
|
|
therefore be wrong for some bundles, which is exactly what happened before it was
|
|
|
|
|
removed: a real export understated its own per-minute window by 59 days. Use the
|
|
|
|
|
measured `first_period` and `last_period` in the Inventory block for what this bundle
|
|
|
|
|
actually covers, `distinct_periods` against that span for how much of it is present,
|
|
|
|
|
and do not prune a query against an assumed retention.
|
|
|
|
|
|
|
|
|
|
The event log is trimmed by count, not age: roughly the newest 100 records **per**
|
|
|
|
|
**`tag`**, older ones deleted as new arrive, so a busy channel reaches back less far
|
|
|
|
|
than a quiet one on the same device. A short file is evidence of that trimming, not
|
|
|
|
|
of inactivity, and the earliest row in a file is a trimming boundary rather than a
|
|
|
|
|
device's first day.
|
|
|
|
|
|
|
|
|
|
**3. Withheld — held by the platform, not included in this copy.** Unlike
|
|
|
|
|
reason 2, these records exist right now. Nothing has deleted them and a further
|
|
|
|
|
request can produce them.
|
|
|
|
|
|
|
|
|
|
No event-log records were withheld from this export: every record matching the device
|
|
|
|
|
is in the file.
|
|
|
|
|
|
|
|
|
|
**4. Never versioned — stored as a current setting, with no history.** Tariff names
|
|
|
|
|
and rates, and the account currency, are each a single value overwritten on change.
|
|
|
|
|
No history exists, so no snapshot of them could be attributed to a historical row and
|
|
|
|
|
none is included. What *is* attributable is `energy_price`, computed per row at write
|
|
|
|
|
time; `energy_price / f3` recovers the rate that applied then. The currency in force
|
|
|
|
|
when this export ran is in Provenance and applies to that moment only.
|
|
|
|
|
|
|
|
|
|
**5. Operational artefacts — the platform's data, not the subject's.**
|
|
|
|
|
device `ACTIONS` (Gen1 webhook configuration) and `CACHE` (BLE cloud-relay
|
|
|
|
|
bookkeeping), and stock artwork shipped with the app.
|
|
|
|
|
|
|
|
|
|
## Provenance
|
|
|
|
|
|
|
|
|
|
- Generated 2026-10-23T07:03:32.760Z
|
|
|
|
|
- Time zone: Europe/Vienna (from the home) - every timestamp in the data files is UTC regardless
|
|
|
|
|
- Built from splitty b669c97 (packed)
|
|
|
|
|
- Home `2531873`, device `XLQd6aExkzDOkdyDoV0KQer`
|
|
|
|
|
- Event log from `event_logs` (version 1)
|
|
|
|
|
- Account tier at export time: free
|
|
|
|
|
- Device state from `DEV:XLQd6aExkzDOkdyDoV0KQer:*` (local Redis db 0): INFO, STATUS, SETTINGS present
|
|
|
|
|
- Secret-shaped values blanked in `last_known_state.json`: 0
|
|
|
|
|
- Export scope: limited - the platform's 16 `_1m` and 14 raw series (its whole sub-hourly inventory, whatever subset of it this device writes) were not queried
|
|
|
|
|
- Event log: all 4 matching records included (cap 5, not reached)
|
|
|
|
|
- A `.sha256` sidecar sits next to the archive; the archive cannot contain its own
|
|
|
|
|
checksum, so verify with `sha256sum -c <name>.zip.sha256`
|