updated apps

This commit is contained in:
2026-07-14 23:57:03 -04:00
parent 6cc7212cef
commit 010e828e9c
797 changed files with 45153 additions and 4246 deletions
@@ -131,6 +131,10 @@ def _build_task_summary(
"nfc_tag_id": task_data.get("nfc_tag_id"),
# v2.20 (#83): unit for `reading`-type tasks; values live in history.
"reading_unit": task_data.get("reading_unit"),
# Spare parts: consumption links ([{part_id, quantity}]) and, on an
# auto-created "buy" reminder, the owning part marker ({part_id}).
"consumes_parts": task_data.get("consumes_parts"),
"part_ref": task_data.get("part_ref"),
"priority": task_data.get("priority", "normal"),
# v2.10.0 archive: archived_at is the persisted timestamp (None = active);
# `archived` is the convenience bool the frontend filters on; reason is
@@ -229,12 +233,44 @@ def _build_object_response(hass: HomeAssistant, entry: ConfigEntry, coordinator_
tasks = [_build_task_summary(hass, tid, tdata, ct_tasks.get(tid), object_slug) for tid, tdata in tasks_data.items()]
# (roadmap P2) attached-document count for the objects-table paperclip badge.
# (roadmap P2) attached-document count for the objects-table paperclip badge,
# plus a per-task count so each task row can carry its own document badge.
from .. import DOCUMENT_STORE_KEY
doc_store = hass.data.get(DOMAIN, {}).get(DOCUMENT_STORE_KEY)
object_id = obj_data.get("id", "")
document_count = len(doc_store.for_object(object_id)) if doc_store is not None and object_id else 0
object_docs = doc_store.for_object(object_id) if doc_store is not None and object_id else []
document_count = len(object_docs)
task_doc_counts: dict[str, int] = {}
for _doc in object_docs:
for _tid in _doc.get("task_ids") or []:
task_doc_counts[_tid] = task_doc_counts.get(_tid, 0) + 1
for _task in tasks:
_task["document_count"] = task_doc_counts.get(_task["id"], 0)
# Spare parts: full definition + merged stock + the derived helpers the
# panel renders (is_low, resolved shopping URL). EVERY persisted field is
# exposed (#50 field-completeness class).
from ..const import CONF_PART_SEARCH_URL_TEMPLATE, CONF_PARTS
from ..helpers.global_options import get_global_options
from ..helpers.i18n import normalize_language
from ..helpers.parts import part_is_low, resolve_shopping_url
rd_parts = getattr(entry, "runtime_data", None)
part_store = getattr(rd_parts, "store", None) if rd_parts else None
search_template = get_global_options(hass).get(CONF_PART_SEARCH_URL_TEMPLATE)
lang = normalize_language(hass)
parts_payload = []
for part in (entry.data.get(CONF_PARTS) or {}).values():
stock = part_store.get_part_stock(part["id"]) if part_store else None
parts_payload.append(
{
**part,
"stock": stock,
"is_low": part_is_low(part, stock),
"shopping_url": resolve_shopping_url(part, search_template, lang),
}
)
return {
"entry_id": entry.entry_id,
@@ -274,15 +310,15 @@ def _build_object_response(hass: HomeAssistant, entry: ConfigEntry, coordinator_
"document_count": document_count,
},
"tasks": tasks,
"parts": parts_payload,
}
def _get_global_entry(hass: HomeAssistant) -> ConfigEntry | None:
"""Get the global config entry."""
for entry in hass.config_entries.async_entries(DOMAIN):
if entry.unique_id == GLOBAL_UNIQUE_ID:
return entry
return None
"""Get the global config entry (thin re-export of the shared helper)."""
from ..helpers.global_options import get_global_entry
return get_global_entry(hass)
def _load_object_entry(
@@ -434,6 +470,21 @@ def async_register_commands(hass: HomeAssistant) -> None:
ws_unarchive_object,
ws_update_object,
)
from .parts import (
ws_create_part,
ws_delete_part,
ws_restock_part,
ws_update_part,
)
from .problem_sensors import (
ws_adopt_problem_sensors,
ws_discover_problem_sensors,
)
from .saved_views import (
ws_delete_saved_view,
ws_list_saved_views,
ws_save_saved_view,
)
from .tags import ws_list_tags
from .tasks import (
ws_archive_task,
@@ -486,6 +537,10 @@ def async_register_commands(hass: HomeAssistant) -> None:
websocket_api.async_register_command(hass, ws_reset_task)
websocket_api.async_register_command(hass, ws_snooze_task)
websocket_api.async_register_command(hass, ws_postpone_task)
websocket_api.async_register_command(hass, ws_create_part)
websocket_api.async_register_command(hass, ws_update_part)
websocket_api.async_register_command(hass, ws_delete_part)
websocket_api.async_register_command(hass, ws_restock_part)
websocket_api.async_register_command(hass, ws_update_history_entry)
websocket_api.async_register_command(hass, ws_get_templates)
websocket_api.async_register_command(hass, ws_export_data)
@@ -494,6 +549,11 @@ def async_register_commands(hass: HomeAssistant) -> None:
websocket_api.async_register_command(hass, ws_export_objects_csv)
websocket_api.async_register_command(hass, ws_import_csv)
websocket_api.async_register_command(hass, ws_import_json)
websocket_api.async_register_command(hass, ws_discover_problem_sensors)
websocket_api.async_register_command(hass, ws_adopt_problem_sensors)
websocket_api.async_register_command(hass, ws_list_saved_views)
websocket_api.async_register_command(hass, ws_save_saved_view)
websocket_api.async_register_command(hass, ws_delete_saved_view)
websocket_api.async_register_command(hass, ws_get_groups)
websocket_api.async_register_command(hass, ws_create_group)
websocket_api.async_register_command(hass, ws_update_group)