From cd324e882ebe93e4a18dfaa7a1e180972db9af93 Mon Sep 17 00:00:00 2001 From: "a.edenhauser" Date: Tue, 27 Oct 2026 07:56:02 +0100 Subject: [PATCH] add: lock data --- lock_data/after_init/AGENTS.md | 515 +++++++++++++++++++++ lock_data/after_init/README.md | 219 +++++++++ lock_data/after_init/device.json | 17 + lock_data/after_init/event_log.csv | 5 + lock_data/after_init/last_known_state.json | 47 ++ 5 files changed, 803 insertions(+) create mode 100644 lock_data/after_init/AGENTS.md create mode 100644 lock_data/after_init/README.md create mode 100644 lock_data/after_init/device.json create mode 100644 lock_data/after_init/event_log.csv create mode 100644 lock_data/after_init/last_known_state.json diff --git a/lock_data/after_init/AGENTS.md b/lock_data/after_init/AGENTS.md new file mode 100644 index 0000000..6162a43 --- /dev/null +++ b/lock_data/after_init/AGENTS.md @@ -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_` | `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; +`_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_` | +| `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::` 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_` | +| `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 .zip.sha256` diff --git a/lock_data/after_init/README.md b/lock_data/after_init/README.md new file mode 100644 index 0000000..7da8423 --- /dev/null +++ b/lock_data/after_init/README.md @@ -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_` | `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 .zip.sha256` (Linux) or +`shasum -a 256 -c .zip.sha256` (macOS) in the folder holding the archive. diff --git a/lock_data/after_init/device.json b/lock_data/after_init/device.json new file mode 100644 index 0000000..2d53376 --- /dev/null +++ b/lock_data/after_init/device.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/lock_data/after_init/event_log.csv b/lock_data/after_init/event_log.csv new file mode 100644 index 0000000..04ecfc2 --- /dev/null +++ b/lock_data/after_init/event_log.csv @@ -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""]" diff --git a/lock_data/after_init/last_known_state.json b/lock_data/after_init/last_known_state.json new file mode 100644 index 0000000..d1d2254 --- /dev/null +++ b/lock_data/after_init/last_known_state.json @@ -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": [] +} \ No newline at end of file