Class Items
This class implements the following methods and properties:
- class lib.item.Items(smarthome)[Quellcode]
Bases:
objectItems loader class. (Item-methods from bin/smarthome.py are moved here.)
An instance is created during initialization by bin/smarthome.py
There should be only one instance of this class. So: Don’t create another instance
- Parameter:
smarthome (object) – Instance of the smarthome master-object
- plugin_attributes = {}
- plugin_attribute_prefixes = {}
- plugin_prefixes_tuple = None
- structs = None
- static get_instance()[Quellcode]
Returns the instance of the Items class, to be used to access the items-api
Use it the following way to access the api:
from lib.item import Items items = Items.get_instance() # to access a method (eg. return_items()): items.return_items()
- Rückgabe:
items instance
- Rückgabetyp:
object
- add_struct_definition(plugin_name, struct_name, struct, from_dir='plugins', optional=False)[Quellcode]
- return_struct_definitions(all=True)[Quellcode]
Return all loaded structure template definitions
- Rückgabe:
- Rückgabetyp:
dict
- load_itemdefinitions(env_dir, items_dir, etc_dir, plugins_dir)[Quellcode]
Load item definitions
This method is called during initialization of SmartHomeNG to initialize the item tree. For that, it loads the item definitions from ../items directory through calling the function parse_itemsdir() from lib.config
- Parameter:
env_dir (str) – path to the directory containing the core’s environment item definition files
items_dir (str) – path to the directory containing the user’s item definition files
etc_dir (str) – path to the directory containing the user’s configuration files (only used for ‚struct‘ support)
plugins_dir (str) – path to the directory containing the plugins (only used for ‚struct‘ support)
- create_item(path, config, parent=None, persist=True, filename=None, create_missing_parents=False)[Quellcode]
Create a single item at runtime and fully initialize it (and any nested child items declared in config).
Unlike load_itemdefinitions(), which batches the init phases across the whole tree (to support forward references between items being loaded together), this runs the init phases immediately, scoped to only the newly created item and its descendants. A pre-existing item with a wildcard
trigger:pattern that would now match this new item is not retroactively rewired — that would require re-running _init_prerun() across the whole tree on every creation.If parent is not given and path has a dotted parent segment, that parent is resolved by path. A missing parent raises ValueError unless create_missing_parents is set, in which case every missing ancestor is created first (in order, shallow to deep) as an empty item — same as calling create_item() on each of them individually with an empty config.
- Parameter:
path (str) – Full path of the item to create
config (dict) – Attribute configuration dict for the item
parent – Item under which to create this item; None to resolve the parent from path (or for a top-level item, if path has no dot)
persist (bool) – If True (default), also write config to a yaml file in items_dir, so the item survives a restart. If False, the item is runtime-only, same as before this parameter existed.
filename (str) – Basename (without extension) of the yaml file to persist to. Only used when persist is True; falls back to
sh._created_items_fileif not given. Auto-created missing parents use the same persist/filename as path itself.create_missing_parents (bool) – If True, auto-create any missing ancestor of path (as an empty item) instead of raising ValueError.
- Rückgabe:
The newly created Item, or None if its own name collided with an existing attribute and was dropped — see lib.item._internal._parsing.check_item_name_collision(). A collision on an auto-created ancestor instead raises ValueError, since silently dropping just an ancestor would leave the requested item never created but reported as if the failure were about path itself.
- Rückgabetyp:
Item or None
- add_item(path, item)[Quellcode]
Function to to add an item to the dictionary of items. If the path does not exist, it is created
- Parameter:
path (str) – Path of the item
item (object) – The item itself
- edit_item(item, config)[Quellcode]
Edit an existing item’s attributes at runtime, in place — preserves Python object identity (unlike create_item()/remove_item()), so other items‘ incoming trigger/hysteresis_input registrations onto item, and this item’s own value/history/children, all survive automatically without any rewiring step. See ~/.claude/handoff/shng-edit-item-attributes.md for the full design rationale.
config is the COMPLETE new attribute set, same convention as create_item() — omitting a key resets it to its default, there is no separate partial-patch/delete-sentinel scheme.
Other items‘ incoming trigger/hysteresis_input registrations onto item live on THIS item’s own _items_to_trigger list, which an edit never resets — they survive an edit untouched, with no special-case code needed here.
- Parameter:
item (object) – The item to edit
config (dict) – Complete new attribute configuration dict
- Rückgabe:
The same item, mutated
- Rückgabetyp:
- remove_item(item, persist=True, recursive=False)[Quellcode]
Function to remove an item from the dictionary of items and delete the item object.
An item with sub-items is left untouched (raises ValueError) unless recursive is set — sub-items are not necessarily defined in the same yaml file as their parent (each item tracks its own
_filenameindependently), so this can’t be handled as a side effect of removing the parent’s own yaml node; every descendant is removed individually, deepest first, each via its own source file.- Parameter:
item (object) – The item to delete
persist (bool) – If True (default), also remove the item’s entry from the yaml file it was defined in (
item. property.defined_in/item._filename), if any. Works for any item with a known source file, not only ones created via create_item(persist=True) — deliberately generic. No-op if the item has no known source file. Applies to every removed descendant too, each using its own source file.recursive (bool) – If True, also remove every sub-item first (deepest first). If False (default) and item has sub-items, raises ValueError instead of removing anything.
- rename_item(item, new_path, filename=None)[Quellcode]
Rename an item in place, optionally moving it to a new parent (see ~/.claude/handoff/shng-rename-item-design.md). Mutates the item’s own path (and, for a move, its parent) and re-keys it in __item_dict; unlike edit_item(), the item’s attribute config is untouched, only its identity (path/parent).
If the item is persisted, its YAML node moves to: filename if given explicitly; otherwise the new parent’s file, if the new parent is a real (non-top-level) Item with one; otherwise the item’s own current file unchanged (same-file rename, the original v1 behavior). A non-persisted item is never persisted as a side effect of a move.
- Parameter:
item – The item to rename/move
new_path (str) – The item’s new full path — same parent segment for a plain rename, a different one to move it
filename (str) – Explicit target yaml file (basename, no extension) to override the default above
- Rückgabe:
The same item, mutated
- Rückgabetyp:
- copy_item(item, new_path, filename=None, create_missing_parents=False, include_children=True)[Quellcode]
Copy an item to new_path as an independent clone — fresh plugin bindings and value history, no shared identity with the source (unlike rename_item(), which preserves identity and only relocates it). Copies the item’s entire subtree by default; set include_children to False to copy only the item itself.
Only persisted items can be copied: there is no existing helper that reconstructs a full nested (with-children) config dict from a live, non-persisted item tree — current_config_for_edit() strips child (dict-valued) keys on purpose, since edit_item() never touches children. A non-persisted source raises ValueError.
The copy is written to filename if given, otherwise to the SOURCE item’s own file — deliberately not the new parent’s file the way rename_item() defaults when moving, since a copy is meant to be „the same config, elsewhere,“ not adopt its new neighbourhood’s file.
Self-references inside the copied subtree (eval/on_change/ on_update/trigger/hysteresis_input/cycle/autotimer/ hysteresis_upper_threshold/hysteresis_lower_threshold) are handled differently depending on whether their target is actually part of what’s being copied:
An absolute (
sh.<path>/bare-path) reference whose target IS part of the copy (old_path itself, or one of its descendants when include_children brought it along) is rewritten from old_path to new_path, via the same boundary-aware prefix replace rename_item() uses for external references.An absolute reference whose target is NOT part of the copy (a descendant include_children=False left behind, or something entirely outside old_path’s subtree) is left pointing at the original — reported back in
left_pointing_at_originalrather than silently rewritten to a path that won’t exist, or silently dropped.A relative reference (leading-dot syntax, resolved fresh against the item’s own position at every load) is NEVER rewritten — its target is inherently tied to tree position, so „leaving it alone“ IS the text staying byte-for-byte the same. It’s classified instead: silently fine if it still resolves inside the copied tree; always reported in
relative_references_flaggedif its original target was a descendant that wasn’t copied along (guaranteed broken); and, if its target was outside old_path’s subtree entirely, reported only if re-resolving the same text from the new position lands on a different target than before (can’t tell whether that drift is a bug or intentional, so it’s surfaced rather than guessed at).
- Parameter:
item – The item to copy
new_path (str) – Full path for the copy
filename (str) – Explicit target yaml file (basename, no extension) to override the default (source item’s own file)
create_missing_parents (bool) – If True, auto-create any missing ancestor of new_path instead of raising ValueError — see create_item().
include_children (bool) – If False, copy only the item’s own attributes — none of its child items.
- Rückgabe:
(copy, report) — copy is the newly created copy, or None if its own name collided with an existing attribute and was dropped (see create_item()); report is {„left_pointing_at_original“: […], „relative_references_flagged“: […]}, each entry a dict with item/attribute/reference (plus a reason and the resolved old/new targets for relative entries) — see _rewrite_subtree_self_references().
- Rückgabetyp:
tuple(Item or None, dict)
- find_references(path)[Quellcode]
Best-effort search for textual references to item path inside other items‘
eval,on_change,on_update,triggerandhysteresis_inputattributes.This is a review aid, not a safety mechanism: a reference embedded in free-form
evaltext cannot be tracked structurally (unliketrigger/hysteresis_input, which already are, via_items_to_trigger/_hysteresis_items_to_trigger— those are included here too, for a complete picture in one place). There is no guarantee of completeness (e.g. a computed/concatenated reference won’t be found) and no guarantee against false positives beyond a word-boundary match. Intended to be called interactively before deleting an item, so a human can review the result — it is deliberately not wired into remove_item() itself.- Parameter:
path (str) – Path of the item to search for
- Rückgabe:
List of (item, attribute_name, attribute_value, unambiguous) tuples, one per match.
unambiguousis True if path is the only item the matched text depends on (see _is_unambiguous_reference()) — a hint for which matches might be safe to auto-clean later, not a guarantee.- Rückgabetyp:
list
- current_config_for_edit(item)[Quellcode]
Best-effort reconstruction of item’s current complete attribute config, suitable as a base for an edit_item() call — there is no single source of truth for this on a live Item (core attributes like eval/trigger/type live in dedicated fields, not item.conf, see Item._apply_config()).
If item is persisted, its on-disk YAML entry IS that complete config (exactly what create_item()/edit_item() last wrote there) — used directly, with any child-item blocks (dict-valued keys) stripped, since edit_item() handles children separately. If not persisted, falls back to reading the known core fields plus item.conf — accurate for every attribute find_references() can detect, but may not preserve more obscure attributes that were never written to a config dict in the first place.
Used internally by remove_references()/rename_item() to build a referencing item’s new config, and exposed cross-module (e.g. via modules/admin/itemdata.py’s „editable_config“ field) so a frontend can safely pre-populate an edit-attributes form — item.conf alone (the „config“ field there) never includes core attributes, only generic/plugin ones, so it’s unsafe to PATCH back as-is.
- Parameter:
item – The item to read the current config for
- Rückgabe:
Attribute configuration dict
- Rückgabetyp:
dict
- remove_references(path)[Quellcode]
Strip dangling unambiguous references to path from every other item, via find_references()/edit_item() — intended to be called right before deleting the item at path, so other items aren’t left pointing at something that no longer exists.
Ambiguous references (something else the referencing attribute also depends on) are left untouched and reported back, not treated as an error — there is no safe automatic action for them.
trigger/hysteresis_inputmatches are mechanical (the whole matched value IS the bare path) — the trigger list entry is filtered out (dropping the key if the list becomes empty), or hysteresis_input is cleared.evalis a single freeform expression — the entire attribute is cleared, since a substring can’t be safely excised from arbitrary Python.on_change/on_updateare lists of independent freeform expressions, liketriggerstructurally — only the matching list entry is dropped, the rest of the list survives.A referencing item with multiple dangling attributes (e.g. both
evalandtriggerpointing at path) gets ONE edit_item() call with all of its changes combined, not one call per attribute.- Parameter:
path (str) – Path of the item whose incoming references should be cleaned up
- Rückgabe:
{„removed“: [(item_path, [attribute_names])], „skipped_ambiguous“: [(item_path, attribute_name, value)]}
- Rückgabetyp:
dict
- get_toplevel_items()[Quellcode]
Returns a list with all items defined at the top level
- Rückgabe:
items defined at the top level
- Rückgabetyp:
list
- return_item(string)[Quellcode]
Function to return the item for a given path
- Parameter:
string (str) – Path of the item to return
- Rückgabe:
Item
- Rückgabetyp:
object
- return_items(ordered=False)[Quellcode]
Function to return a list with all defined items
- Parameter:
ordered (bool) – return list sorted alphabetically, defaults to False
- Rückgabe:
List of all items
- Rückgabetyp:
list
- match_items(regex)[Quellcode]
Function to match items against a regular expression
- Parameter:
regex (str) – Regular expression to match items against
- Rückgabe:
List of matching items
- Rückgabetyp:
list
- find_items(conf)[Quellcode]
Function to find items that match the specified configuration
- Parameter:
conf (str) – Configuration to look for
- Rückgabe:
list of matching items
- Rückgabetyp:
list
- find_children(parent, conf)[Quellcode]
Function to find children with the specified configuration
- Parameter:
parent (str) – parent item on which to start the search
conf (str) – Configuration to look for
- Rückgabe:
list or matching child-items
- Rückgabetyp:
list
- item_count()[Quellcode]
Return the number of defined items
- Rückgabe:
number of items
- Rückgabetyp:
int
- stop(signum=None, frame=None)[Quellcode]
Stop what all items are doing
At the moment, it stops fading of all items
- add_plugin_attribute(plugin_name, attribute_name, attribute)[Quellcode]
Add an attribute definition to the dict of plugin specific item-attributes
- Parameter:
plugin_name – Name of the plugin that defines the attribute
attribute_name – Name of the attribute
attribute – Metadata that defines the attribute
- Rückgabe:
- add_plugin_attribute_prefix(plugin_name, prefix_name, prefix)[Quellcode]
Add an attribute-prefix definition to the dict of plugin specific item-attribute prefixes
- Parameter:
plugin_name – Name of the plugin that defines the attribute
prefix_name – Name of the attribute-prefix
prefix – Metadata that defines the attribute-prefix
- Rückgabe:
- plugin_attribute_exists(attribute_name)[Quellcode]
Returns the type of the attribute’s value
- Parameter:
attribute_name – Name of the attribute
- Rückgabe:
Type of the attribute’s value or None
- get_plugin_attribute_type(attribute_name)[Quellcode]
Returns the type of the attribute’s value
- Parameter:
attribute_name – Name of the attribute
- Rückgabe:
Type of the attribute’s value or None