add: lock data

This commit is contained in:
2026-10-27 07:56:02 +01:00
parent 96f148a770
commit cd324e882e
5 changed files with 803 additions and 0 deletions
+515
View File
@@ -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`
+219
View File
@@ -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.
+17
View File
@@ -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"
}
]
}
+5
View File
@@ -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""]"
1 tag t e p
2 XLQd6aExkzDOkdyDoV0KQer 1792566653259 18 ["XLQd6aExkzDOkdyDoV0KQer","night_locking"]
3 XLQd6aExkzDOkdyDoV0KQer 1792566670773 18 ["XLQd6aExkzDOkdyDoV0KQer","day_locking"]
4 XLQd6aExkzDOkdyDoV0KQer 1792566698562 18 ["XLQd6aExkzDOkdyDoV0KQer","day_lock"]
5 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": []
}