add: lock data
This commit is contained in:
@@ -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`
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
# Your device data
|
||||||
|
|
||||||
|
This folder contains the data we hold about one of your Shelly devices, a
|
||||||
|
**LOQED**. It was prepared on 2026-10-23 in response to your request.
|
||||||
|
|
||||||
|
Every date and time in these files is **UTC**, which may differ from your local time.
|
||||||
|
|
||||||
|
## Your device
|
||||||
|
|
||||||
|
First seen on our systems: 2026-10-21 07:10:47
|
||||||
|
|
||||||
|
This device has a single channel.
|
||||||
|
|
||||||
|
| Channel | Your name for it | Identifier |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 0 | Back door | `XLQd6aExkzDOkdyDoV0KQer` |
|
||||||
|
|
||||||
|
### The rooms these files mention
|
||||||
|
|
||||||
|
`room_id` appears in `device.json` as a number. These
|
||||||
|
are the ones your device's own records use:
|
||||||
|
|
||||||
|
| `room_id` | Room |
|
||||||
|
| --- | --- |
|
||||||
|
| 2 | Lab |
|
||||||
|
|
||||||
|
More than one room can appear for the same channel: the value recorded is the room
|
||||||
|
the channel was in **at the time**, so moving a device leaves older rows pointing at
|
||||||
|
where it used to be. Only rooms your device appears in are listed here.
|
||||||
|
|
||||||
|
## What is in this folder
|
||||||
|
|
||||||
|
| File | What it is |
|
||||||
|
| --- | --- |
|
||||||
|
| `device.json` | How the device was set up |
|
||||||
|
| `event_log.csv` | Things the device did — 4 record(s) |
|
||||||
|
| `last_known_state.json` | How the device was set up and what it last reported |
|
||||||
|
| `statistics/` | Empty — see “What is not here, and why” |
|
||||||
|
| `README.md` | This file |
|
||||||
|
|
||||||
|
| `AGENTS.md` | The same information written for software — precise formats and full |
|
||||||
|
| | reference tables |
|
||||||
|
|
||||||
|
**Start with `event_log.csv`** if you just want an overview — for this device it is
|
||||||
|
the record of what happened, and there are no measurements to read alongside it.
|
||||||
|
|
||||||
|
## Using an AI assistant, or a database
|
||||||
|
|
||||||
|
These files work well with an AI assistant, and `AGENTS.md` in this folder is written
|
||||||
|
for exactly that — point the tool at the folder and it will find it.
|
||||||
|
|
||||||
|
One thing to weigh first: this data describes your home and when you are in it, and
|
||||||
|
most AI services upload whatever you give them to their own servers. Pick one you
|
||||||
|
trust with that, or run a model on your own machine.
|
||||||
|
|
||||||
|
For anything more than a quick look, load the CSV files into **SQLite**. It handles
|
||||||
|
the largest of these files without complaining, and there are free tools with a normal
|
||||||
|
windowed interface — DB Browser for SQLite, or DBeaver. Nothing leaves your computer.
|
||||||
|
`AGENTS.md` has the exact commands.
|
||||||
|
|
||||||
|
## `device.json`
|
||||||
|
|
||||||
|
How the device and each of its channels were configured: the name you gave it, the
|
||||||
|
room you put it in and its model.
|
||||||
|
`image` is the picture shown in the app — where that points at
|
||||||
|
`assets/user_images/...` the file is in `images/`; anything else is stock artwork
|
||||||
|
supplied with the app and is not personal data.
|
||||||
|
|
||||||
|
## `last_known_state.json`
|
||||||
|
|
||||||
|
How this device was configured, and the last thing it reported to us before this
|
||||||
|
export was prepared. It is a snapshot, not a history — if the device has been
|
||||||
|
offline, this is how it looked when we last heard from it.
|
||||||
|
|
||||||
|
**It can mention your other devices by id.** Some devices depend on another one to
|
||||||
|
reach us — a battery sensor on Bluetooth, for instance, is relayed by a
|
||||||
|
mains-powered device nearby, and its state names the one that relayed it. Those
|
||||||
|
ids identify devices of yours, not anybody else's, and nothing those devices
|
||||||
|
recorded is in this folder.
|
||||||
|
|
||||||
|
## `event_log.csv`
|
||||||
|
|
||||||
|
4 record(s), from 2026-10-21T07:10:53.259Z to 2026-10-21T07:22:58.358Z.
|
||||||
|
|
||||||
|
| Column | Meaning |
|
||||||
|
| --- | --- |
|
||||||
|
| `tag` | Which channel of the device the record belongs to |
|
||||||
|
| `t` | When it happened, in **milliseconds since 1 January 1970 (UTC)** |
|
||||||
|
| `e` | What happened — see the table below |
|
||||||
|
| `p` | The details of the event |
|
||||||
|
|
||||||
|
To turn `t` into a date: divide by 1000 and read the result as a Unix timestamp. In a
|
||||||
|
spreadsheet, `=(t/86400000)+DATE(1970,1,1)` gives a normal date and time.
|
||||||
|
|
||||||
|
**This log is not a full history.** We keep about the newest 100 records for each
|
||||||
|
channel and delete older ones as new ones arrive, so it reaches back weeks or months
|
||||||
|
rather than years — and a busy channel reaches back less far than a quiet one.
|
||||||
|
|
||||||
|
### What the `e` values in your file mean
|
||||||
|
|
||||||
|
| `e` | Meaning |
|
||||||
|
| --- | --- |
|
||||||
|
| 18 | A lock was locked or unlocked |
|
||||||
|
|
||||||
|
Only the values that occur in your file are listed. Other kinds of device produce
|
||||||
|
other kinds of record.
|
||||||
|
|
||||||
|
### Reading the `p` column
|
||||||
|
|
||||||
|
In this export the details are stored as a **list, in a fixed order** — the names are
|
||||||
|
not repeated on every row, to keep the file small. Read the values positionally:
|
||||||
|
|
||||||
|
**`e` = 18** — A lock was locked or unlocked
|
||||||
|
|
||||||
|
| Position | Name | Meaning |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| 1 | `deviceId` | the device this event came from |
|
||||||
|
| 2 | `state` | the new state |
|
||||||
|
|
||||||
|
So a row reading `["c8f09e87cea0",0,true]` against `e` = 1 means: this device,
|
||||||
|
channel 0, switched on.
|
||||||
|
|
||||||
|
## Questions you might have
|
||||||
|
|
||||||
|
**Why does the time not match my clock?** All times are UTC.
|
||||||
|
Your time zone is **Europe/Vienna**, so a row stamped `2026-01-15T22:00:00Z`
|
||||||
|
is 15 January, late evening, where you are — and a row stamped just after midnight
|
||||||
|
UTC may belong to the previous day locally. Spreadsheets do not convert this for
|
||||||
|
you: add your offset to the value before grouping by day, or the first hours of
|
||||||
|
each local day land in the wrong one.
|
||||||
|
|
||||||
|
**Some readings look wrong.** Very likely they are — but they are what your device
|
||||||
|
sent us, and we have exported it unaltered rather than tidying it up. A device that
|
||||||
|
has failed can keep reporting nonsense while it is still listed on your account.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How this export was produced
|
||||||
|
|
||||||
|
Device `XLQd6aExkzDOkdyDoV0KQer`, also written `XLQd6aExkzDOkdyDoV0KQer` in some files. Home `2531873`.
|
||||||
|
Generated 2026-10-23T07:03:32.760Z.
|
||||||
|
Times below are UTC. This home is set to
|
||||||
|
**Europe/Vienna**.
|
||||||
|
|
||||||
|
The same device is identified two ways depending on the file, which is why both forms
|
||||||
|
appear above:
|
||||||
|
|
||||||
|
| Form | Value | Used in |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Decimal | `XLQd6aExkzDOkdyDoV0KQer` | our internal records for this device |
|
||||||
|
| Hexadecimal | `XLQd6aExkzDOkdyDoV0KQer`, `XLQd6aExkzDOkdyDoV0KQer_<n>` | `device.json`, the event log `tag` |
|
||||||
|
|
||||||
|
| File | Selected by | Amount |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `device.json` | the identifiers above | 1 channel(s) |
|
||||||
|
| `event_log.csv` | the event `tag` | 4 record(s) |
|
||||||
|
| `statistics/` | the `device_id` tag | 0 row(s) in 0 file(s) |
|
||||||
|
| `images/` | the `image` setting of each channel | 0 file(s) |
|
||||||
|
|
||||||
|
The event log was read from `event_logs` (version 1), covering
|
||||||
|
2026-10-21T07:10:53.259Z to 2026-10-21T07:22:58.358Z.
|
||||||
|
|
||||||
|
This export was made to match what your **free plan** provides.
|
||||||
|
|
||||||
|
- The event log holds your 5 most recent records.
|
||||||
|
|
||||||
|
### What is not here, and why
|
||||||
|
|
||||||
|
Something you expected may be missing for six quite different reasons.
|
||||||
|
They are not the same, and the difference matters:
|
||||||
|
|
||||||
|
**1. It is not part of a device export.** This folder is about one device. We still
|
||||||
|
hold the following — it was left out on purpose, not lost, and you can ask for it:
|
||||||
|
|
||||||
|
- your account details: name, email, language, display preferences, billing
|
||||||
|
- your home's layout — room definitions, dashboards and groups
|
||||||
|
- automations and scenes, including records of automations running
|
||||||
|
- connections to other services such as Google Home
|
||||||
|
- account ownership and access rights
|
||||||
|
- whole-home monthly energy reports
|
||||||
|
- our internal server records
|
||||||
|
- the data belonging to every other device in your home
|
||||||
|
|
||||||
|
The rooms your device itself appears in are named earlier in this file, because its
|
||||||
|
own records would be unreadable without them.
|
||||||
|
|
||||||
|
**2. It is no longer stored.** Detailed measurements are deleted as they age, and the
|
||||||
|
event log keeps only the most recent records per channel. Anything past that is gone
|
||||||
|
from our systems entirely and cannot be exported by anyone — by us, or by you asking
|
||||||
|
again.
|
||||||
|
How long each level of detail survives is not fixed, so this export does not quote a
|
||||||
|
figure it cannot stand behind.
|
||||||
|
|
||||||
|
**3. We have it, and this copy does not include it.** Nothing was held back from this
|
||||||
|
export — but if you ever receive a copy that is shortened, it will say so here with
|
||||||
|
the numbers, so that a shortened copy can never be mistaken for everything we hold.
|
||||||
|
|
||||||
|
**4. Your device does not record it.** Not every device takes every
|
||||||
|
measurement. A relay without power metering, a door or window sensor, an input or
|
||||||
|
button report what happened rather than measuring anything; and a device that does
|
||||||
|
measure still leaves untouched the columns meant for sensors it does not have.
|
||||||
|
That is why there is no `statistics/` content here at all, and there never was.
|
||||||
|
Nothing was deleted or held back: it was never recorded in the first place, and no
|
||||||
|
request to us can produce it.
|
||||||
|
|
||||||
|
**5. It is a setting, and we keep no record of what it used to be.** Your tariffs and
|
||||||
|
your currency are stored as one current value each: changing one overwrites the old
|
||||||
|
one, and nothing anywhere remembers what it was on a given day.
|
||||||
|
|
||||||
|
**6. It belongs to us rather than to you.** Some of what we store about a device is
|
||||||
|
cloud housekeeping — how it was relayed, what was cached for it — rather than anything
|
||||||
|
about you, and that is left out. Only photographs you uploaded are included; stock
|
||||||
|
artwork supplied with the app is not.
|
||||||
|
|
||||||
|
### Checking the files arrived intact
|
||||||
|
|
||||||
|
A `.sha256` file was produced alongside this archive. To confirm nothing was altered
|
||||||
|
in transit, run `sha256sum -c <name>.zip.sha256` (Linux) or
|
||||||
|
`shasum -a 256 -c <name>.zip.sha256` (macOS) in the folder holding the archive.
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"id": "XLQd6aExkzDOkdyDoV0KQer",
|
||||||
|
"cts": "2026-10-21 07:10:47",
|
||||||
|
"type": "LOQED",
|
||||||
|
"channels": [
|
||||||
|
{
|
||||||
|
"id": "XLQd6aExkzDOkdyDoV0KQer",
|
||||||
|
"name": "Back door",
|
||||||
|
"channel": 0,
|
||||||
|
"channels_count": 1,
|
||||||
|
"room_id": 2,
|
||||||
|
"mode": "",
|
||||||
|
"category": "lock",
|
||||||
|
"image": "images/device_images/LOQED.png"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
"tag","t","e","p"
|
||||||
|
"XLQd6aExkzDOkdyDoV0KQer",1792566653259,18,"[""XLQd6aExkzDOkdyDoV0KQer"",""night_locking""]"
|
||||||
|
"XLQd6aExkzDOkdyDoV0KQer",1792566670773,18,"[""XLQd6aExkzDOkdyDoV0KQer"",""day_locking""]"
|
||||||
|
"XLQd6aExkzDOkdyDoV0KQer",1792566698562,18,"[""XLQd6aExkzDOkdyDoV0KQer"",""day_lock""]"
|
||||||
|
"XLQd6aExkzDOkdyDoV0KQer",1792567378358,18,"[""XLQd6aExkzDOkdyDoV0KQer"",""day_lock""]"
|
||||||
|
@@ -0,0 +1,47 @@
|
|||||||
|
{
|
||||||
|
"info": {
|
||||||
|
"_updated": "2026-10-23 06:23:04",
|
||||||
|
"id": "XLQd6aExkzDOkdyDoV0KQer",
|
||||||
|
"id16": "XLQd6aExkzDOkdyDoV0KQer",
|
||||||
|
"type": "lock",
|
||||||
|
"code": "LOQED",
|
||||||
|
"online": 1,
|
||||||
|
"conntime": 1792736584,
|
||||||
|
"ip": "",
|
||||||
|
"gen": "GXL"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"id": "d6aExkzDOkdyDoV0KQer",
|
||||||
|
"name": "Back door",
|
||||||
|
"model_name": "LOQED Touch",
|
||||||
|
"battery_percentage": -1,
|
||||||
|
"battery_type": "alkaline",
|
||||||
|
"bolt_state": "unknown",
|
||||||
|
"party_mode": false,
|
||||||
|
"guest_access_mode": false,
|
||||||
|
"twist_assist": false,
|
||||||
|
"touch_to_connect": false,
|
||||||
|
"lock_direction": "clockwise",
|
||||||
|
"mortise_lock_type": "cylinder_operated_handle_on_the_outside",
|
||||||
|
"supported_lock_states": [
|
||||||
|
"day_lock",
|
||||||
|
"night_lock"
|
||||||
|
],
|
||||||
|
"online": true,
|
||||||
|
"bridge_ip": "10.12.2.20",
|
||||||
|
"bridge_hostname": "LOQED-8c4b146a1acc.local",
|
||||||
|
"local_id": 0,
|
||||||
|
"serial": 3110,
|
||||||
|
"_updated": "2026-10-23 06:23:04",
|
||||||
|
"_preoffline_battery_percentage": 78,
|
||||||
|
"_preoffline_bolt_state": "day_lock"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"device": {
|
||||||
|
"sleep_mode": false
|
||||||
|
},
|
||||||
|
"_updated": "2026-10-23 06:23:04"
|
||||||
|
},
|
||||||
|
"absent": [],
|
||||||
|
"redacted": []
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user