189 files

This commit is contained in:
Home Assistant Version Control committed 2026-09-08 18:05:32 +00:00
1 parent 79fefc0a7c
commit 303886f212
189 files changed
+21402 -3514

No files matched your search

@@ -21,6 +21,7 @@ from ..const import (
MAX_ID_LENGTH,
MAX_TEXT_LENGTH,
)
from ..helpers.completion_photos import MAX_COMPLETION_PHOTOS, normalize_photo_doc_ids
from ..models.maintenance_task import MaintenanceTask
from . import (
_get_runtime_data,
@@ -97,12 +98,24 @@ def _completion_blocked(rd: Any, task_id: str) -> bool:
# QR deep-link fallback: the panel asserts the scan on a tag-gated task
# whose quick-complete needs the full dialog (bug audit 2026-08-29).
vol.Optional("via_tag_scan"): bool,
# Optional completion photo: the doc_id of an already-uploaded image
# (via the document upload endpoint, tagged "photo").
# Optional completion photos (#161): doc_ids of already-uploaded
# images (via the document upload endpoint, tagged "photo"). The
# scalar form is what pre-2.75 clients send — merged into the list.
vol.Optional("photo_doc_ids"): vol.Any(
vol.All([vol.All(str, vol.Length(max=MAX_ID_LENGTH))], vol.Length(max=MAX_COMPLETION_PHOTOS)),
None,
),
vol.Optional("photo_doc_id"): vol.Any(vol.All(str, vol.Length(max=MAX_ID_LENGTH)), None),
# Meter readings (v2.20, #83): the recorded value for `reading` tasks.
# Wide numeric bounds — meters count high, temperatures go negative.
vol.Optional("reading_value"): vol.Any(vol.All(vol.Coerce(float), vol.Range(min=-1e12, max=1e12)), None),
# #161 phase 2: {slot_id: value} for a task with reading slots; None
# skips a meter this time. Ids are checked against the task's slots
# in the handler (unknown -> invalid_input).
vol.Optional("reading_values"): vol.Any(
{str: vol.Any(vol.All(vol.Coerce(float), vol.Range(min=-1e12, max=1e12)), None)},
None,
),
# Spare parts: on an auto-created "buy" task, how many units were
# actually bought (dialog override of the part's restock_quantity).
vol.Optional("restock_quantity"): vol.Any(vol.All(vol.Any(int, float), vol.Coerce(float), vol.Range(min=0.01, max=9999)), None),
@@ -186,6 +199,24 @@ async def ws_complete_task(
foreign_part_ids=foreign_part_resolver(hass),
)
# #161 phase 2: resolve the slot values into the history snapshot.
reading_values = None
if msg.get("reading_values"):
from ..helpers.reading_slots import resolve_reading_values
from . import _get_merged_tasks
slot_task = _get_merged_tasks(_entry).get(msg["task_id"]) or {}
try:
reading_values = (
resolve_reading_values(
slot_task.get("readings") or [], msg["reading_values"], default_unit=slot_task.get("reading_unit")
)
or None
)
except ValueError as err:
connection.send_error(msg["id"], "invalid_input", str(err))
return
try:
await rd.coordinator.complete_maintenance(
task_id=msg["task_id"],
@@ -194,8 +225,9 @@ async def ws_complete_task(
duration=msg.get("duration"),
checklist_state=msg.get("checklist_state"),
feedback=msg.get("feedback"),
photo_doc_id=msg.get("photo_doc_id"),
photo_doc_ids=normalize_photo_doc_ids(msg.get("photo_doc_ids"), msg.get("photo_doc_id")) or None,
reading_value=msg.get("reading_value"),
reading_values=reading_values,
restock_quantity=msg.get("restock_quantity"),
used_parts=used_parts,
completed_at=completed_at,
@@ -320,8 +352,9 @@ async def ws_skip_task(
as_missed=msg.get("as_missed", False),
)
except ServiceValidationError as err:
# #150: the task carries a skip lock.
connection.send_error(msg["id"], "skip_disabled", str(err))
# #150: the task carries a skip lock; an inactive task keeps its own
# key (task_inactive_skip) so the panel can say why.
connection.send_error(msg["id"], err.translation_key or "skip_disabled", str(err))
return
connection.send_result(msg["id"], {"success": True})