Logbook: Save Item State as a JSON History [5.2.1;5.9.9.9]

This rule template keeps a short history of the state changes of any Item. It stores the history as a JSON array in a String Item. Each time the source Item changes, a new entry with the timestamp and the new state is added at the beginning of the array (newest first). The list is trimmed to a configurable maximum number of entries.

This is useful when you want to show the recent changes of an Item in the UI without querying persistence. Examples are the operating mode of an inverter, the hot water state of a heat pump, or alarm states. The template is based on the idea of Logbook of automatically performed actions - #5 by JustinG . It was built with AI. You can find a suitable widget here JSON History Logbook – list card for Item state histories .

Example of the stored value:

[
  {"time":"2026-09-27T14:32:10.512+0200","event":"Derating Running"},
  {"time":"2026-09-27T09:05:43.120+0200","event":"Running"},
  {"time":"2026-09-27T06:58:01.004+0200","event":"Standby"}
]

Features

• Records every state change of the source Item as {time, event}
• Newest entry first, list limited to a configurable maximum (1–500, default 10)
• Uses the same timestamp format for all entries (yyyy-MM-dd’T’HH:mm:ss.SSSZ)
• Handles NULL, UNDEF and an empty history Item
• Recovers from invalid JSON by resetting the history and logging a warning
• Uses postUpdate only, so the history Item is pure state storage and no commands are sent

Parameter Description
Source Item The Item whose state changes are recorded
History Item (string) String Item that stores the JSON history
Maximum Entries Number of entries to keep (default: 10)

Language: JavaScript

Dependencies:

JS Scripting add-on installed

Changelog

Version 1.0.0

  • Initial release, generalized from two of my own rules

Resources


Summary

\[
{
“uid”: “logbook:item-state-json-history”,
“label”: “Logbook: Save Item Status as a JSON History”,
“description”: “Every time the state of a source item changes, it saves a {time, event} entry at the beginning of a JSON array in a string item and limits the list to a maximum number of entries.”,
“tags”: \[
“Logbook”,
“JSON”,
“Version 1.0.0”
\],
“visibility”: “VISIBLE”,
“configDescriptions”: \[
{
“name”: “sourceItem”,
“type”: “TEXT”,
“context”: “item”,
“required”: true,
“label”: “Source Item”,
“description”: “Item whose state changes are recorded.”
},
{
“name”: “historyItem”,
“type”: “TEXT”,
“context”: “item”,
“filterCriteria”: \[
{
“name”: “type”,
“value”: “String”
}
\],
“required”: true,
“label”: “History Item (String)”,
“description”: “String item that stores the JSON history.”
},
{
“name”: “maxEntries”,
“type”: “INTEGER”,
“defaultValue”: “10”,
“min”: 1,
“max”: 500,
“required”: false,
“label”: “Maximum Entries”,
“description”: “Number of entries to keep.”
}
\],
“triggers”: \[
{
“id”: “1”,
“type”: “core.ItemStateChangeTrigger”,
“configuration”: {
“itemName”: “{{sourceItem}}”
}
}
\],
“conditions”: [ ],
“actions”: \[
{
“id”: “2”,
“type”: “script.ScriptAction”,
“label”: “Update JSON history”,
“configuration”: {
“type”: “application/javascript”,
“script”: “/\*\\n \* Rule template: Item State JSON History\\n \* Stores every state change of a source item as {time, event} at the\\n \* beginning of a JSON array in a String item (newest first).\\n \*\\n \* Version history:\\n \* 1.0.0 (2026-09-27)\\n \* - Initial template\\n \*/\\nconst SOURCE_ITEM_NAME = ‘{{sourceItem}}’;\\nconst HISTORY_ITEM_NAME = ‘{{historyItem}}’;\\nconst MAX_ENTRIES = parseInt(‘{{maxEntries}}’, 10) || 10;\\nconst TIME_FORMAT = time.DateTimeFormatter.ofPattern(“yyyy-MM-dd’T’HH:mm:ss.SSSZ”);\\n\\nconst historyItem = items.getItem(HISTORY_ITEM_NAME);\\nconst sourceItem = items.getItem(SOURCE_ITEM_NAME);\\n\\nfunction isEmptyState(state) {\\n if (state === undefined || state === null) return true;\\n const s = String(state).toUpperCase();\\n return s === ‘NULL’ || s === ‘UNDEF’ || s === ‘’;\\n}\\n\\n// Load existing history, robust against NULL/UNDEF and invalid JSON\\nlet history = [ ];\\nif (!isEmptyState(historyItem.state)) {\\n try {\\n const parsed = JSON.parse(historyItem.state);\\n if (Array.isArray(parsed)) {\\n history = parsed;\\n } else {\\n console.warn(HISTORY_ITEM_NAME + ‘: stored value is not an array, resetting history.’);\\n }\\n } catch (e) {\\n console.warn(HISTORY_ITEM_NAME + ‘: unable to parse JSON (’ + e + ‘), resetting history.’);\\n }\\n}\\n\\n// Prepend new entry\\nhistory.unshift({\\n time: time.ZonedDateTime.now().format(TIME_FORMAT),\\n event: String(sourceItem.state)\\n});\\n\\n// Trim to maximum length\\nif (history.length > MAX_ENTRIES) {\\n history.length = MAX_ENTRIES;\\n}\\n\\n// Pure state storage → postUpdate\\nhistoryItem.postUpdate(JSON.stringify(history));\\n”
}
}
\]
}
\]

3 Likes