updated apps
This commit is contained in:
@@ -37,12 +37,14 @@ from ..const import (
|
||||
MAX_TEXT_LENGTH,
|
||||
MAX_TYPE_LENGTH,
|
||||
MAX_URL_LENGTH,
|
||||
NOTIFICATION_MANAGER_KEY,
|
||||
HistoryEntryType,
|
||||
)
|
||||
from ..helpers.dates import INTERVAL_UNITS
|
||||
from ..helpers.permissions import require_write
|
||||
from ..helpers.schedule import (
|
||||
FLAT_RECURRENCE_KEYS,
|
||||
KIND_INTERVAL,
|
||||
Schedule,
|
||||
normalize_task_storage,
|
||||
)
|
||||
@@ -103,6 +105,8 @@ from .tasks_validation import (
|
||||
vol.Optional("nfc_tag_id"): vol.Any(vol.All(str, vol.Length(max=256)), None),
|
||||
# v2.20 (#83): unit for `reading`-type tasks ("kWh", "m³", ...).
|
||||
vol.Optional("reading_unit"): vol.Any(vol.All(str, vol.Length(max=32)), None),
|
||||
# Spare parts consumed on completion: [{part_id, quantity}].
|
||||
vol.Optional("consumes_parts"): vol.Any(list, None),
|
||||
vol.Optional("priority"): vol.In(TASK_PRIORITIES),
|
||||
vol.Optional("checklist"): vol.Any(
|
||||
vol.All([vol.All(str, vol.Length(max=MAX_CHECKLIST_ITEM_LENGTH))], vol.Length(max=MAX_CHECKLIST_ITEMS)), None
|
||||
@@ -163,7 +167,21 @@ async def ws_create_task(
|
||||
# Recurrence: an explicit nested `schedule` (calendar kinds) takes
|
||||
# precedence; otherwise build from the flat v2.6.x fields.
|
||||
if msg.get("schedule"):
|
||||
task_data["schedule"] = Schedule.from_dict(msg["schedule"]).to_dict()
|
||||
incoming = Schedule.from_dict(msg["schedule"]).to_dict()
|
||||
task_data["schedule"] = incoming
|
||||
# A BARE interval schedule (kind=interval, no `every`) is only the
|
||||
# carrier for the season/ends extras the panel always sends for a
|
||||
# time-based task — the real interval still rides the flat
|
||||
# interval_days / interval_unit / interval_anchor fields. Carry them
|
||||
# over so normalize_task_storage merges them onto the schedule; without
|
||||
# this the interval was dropped entirely (#88 regression).
|
||||
if incoming.get("kind") == KIND_INTERVAL and incoming.get("every") is None:
|
||||
if msg.get("interval_days") is not None:
|
||||
task_data["interval_days"] = msg["interval_days"]
|
||||
if msg.get("interval_unit", "days") != "days":
|
||||
task_data["interval_unit"] = msg["interval_unit"]
|
||||
if msg.get("interval_anchor", "completion") != "completion":
|
||||
task_data["interval_anchor"] = msg["interval_anchor"]
|
||||
else:
|
||||
task_data["schedule_type"] = msg.get("schedule_type", "time_based")
|
||||
if msg.get("interval_days") is not None:
|
||||
@@ -248,6 +266,15 @@ async def ws_create_task(
|
||||
# v2.20 (#83): unit for `reading`-type tasks.
|
||||
if msg.get("reading_unit") is not None:
|
||||
task_data["reading_unit"] = (msg["reading_unit"] or "").strip() or None
|
||||
if msg.get("consumes_parts") is not None:
|
||||
from ..const import CONF_PARTS
|
||||
from ..helpers.parts import sanitize_consumes_parts
|
||||
|
||||
links = sanitize_consumes_parts(
|
||||
msg["consumes_parts"], set(entry.data.get(CONF_PARTS) or {})
|
||||
)
|
||||
if links:
|
||||
task_data["consumes_parts"] = links
|
||||
if msg.get("checklist"):
|
||||
task_data["checklist"] = msg["checklist"]
|
||||
if msg.get("labels"):
|
||||
@@ -276,13 +303,17 @@ async def ws_create_task(
|
||||
connection.send_result(msg["id"], result)
|
||||
return
|
||||
|
||||
await async_persist_task(
|
||||
hass,
|
||||
entry,
|
||||
task_data,
|
||||
last_performed=initial_last_performed,
|
||||
history=initial_history,
|
||||
)
|
||||
try:
|
||||
await async_persist_task(
|
||||
hass,
|
||||
entry,
|
||||
task_data,
|
||||
last_performed=initial_last_performed,
|
||||
history=initial_history,
|
||||
)
|
||||
except ValueError as err:
|
||||
connection.send_error(msg["id"], "limit_reached", str(err))
|
||||
return
|
||||
|
||||
result = {"task_id": task_id}
|
||||
if tc_warnings:
|
||||
@@ -325,6 +356,8 @@ async def ws_create_task(
|
||||
vol.Optional("nfc_tag_id"): vol.Any(vol.All(str, vol.Length(max=256)), None),
|
||||
# v2.20 (#83): unit for `reading`-type tasks ("kWh", "m³", ...).
|
||||
vol.Optional("reading_unit"): vol.Any(vol.All(str, vol.Length(max=32)), None),
|
||||
# Spare parts consumed on completion: [{part_id, quantity}].
|
||||
vol.Optional("consumes_parts"): vol.Any(list, None),
|
||||
vol.Optional("priority"): vol.In(TASK_PRIORITIES),
|
||||
vol.Optional("checklist"): vol.Any(
|
||||
vol.All([vol.All(str, vol.Length(max=MAX_CHECKLIST_ITEM_LENGTH))], vol.Length(max=MAX_CHECKLIST_ITEMS)), None
|
||||
@@ -441,6 +474,7 @@ async def ws_update_task(
|
||||
"custom_icon": "custom_icon",
|
||||
"nfc_tag_id": "nfc_tag_id",
|
||||
"reading_unit": "reading_unit",
|
||||
"consumes_parts": "consumes_parts",
|
||||
"priority": "priority",
|
||||
"checklist": "checklist",
|
||||
"labels": "labels",
|
||||
@@ -464,9 +498,20 @@ async def ws_update_task(
|
||||
or msg.get("schedule_type") in FLAT_SCHEDULE_TYPES
|
||||
)
|
||||
if msg.get("schedule"):
|
||||
for key in FLAT_RECURRENCE_KEYS:
|
||||
task.pop(key, None)
|
||||
task["schedule"] = Schedule.from_dict(msg["schedule"]).to_dict()
|
||||
incoming = Schedule.from_dict(msg["schedule"]).to_dict()
|
||||
task["schedule"] = incoming
|
||||
# A COMPLETE nested schedule is authoritative — drop the flat recurrence
|
||||
# keys so it wins. But a BARE interval schedule (kind=interval with no
|
||||
# `every`) is only the carrier for the season/ends extras the panel
|
||||
# always sends for a time-based task; the real interval still rides the
|
||||
# flat interval_days / interval_unit / interval_anchor fields, which
|
||||
# normalize_task_storage merges onto it. Popping them for the bare
|
||||
# interval dropped the interval entirely (#88 regression from the
|
||||
# season/ends work). Keep them in that one case.
|
||||
bare_interval = incoming.get("kind") == KIND_INTERVAL and incoming.get("every") is None
|
||||
if not bare_interval:
|
||||
for key in FLAT_RECURRENCE_KEYS:
|
||||
task.pop(key, None)
|
||||
elif _flat_recurrence_edit:
|
||||
task.pop("schedule", None)
|
||||
elif "schedule_type" in msg:
|
||||
@@ -567,6 +612,11 @@ async def async_delete_task(
|
||||
return False
|
||||
|
||||
old_trigger_config = new_tasks[task_id].get("trigger_config")
|
||||
# Adopted problem-sensor task? Preserve its notes for a later re-adopt
|
||||
# (no-op for everything else).
|
||||
from ..helpers.problem_sensors import stash_task_notes_for_readopt
|
||||
|
||||
stash_task_notes_for_readopt(hass, new_tasks[task_id])
|
||||
del new_tasks[task_id]
|
||||
new_data[CONF_TASKS] = new_tasks
|
||||
|
||||
@@ -585,7 +635,7 @@ async def async_delete_task(
|
||||
await store.async_save()
|
||||
|
||||
# Clean up notification state for deleted task
|
||||
nm = hass.data.get(DOMAIN, {}).get("_notification_manager")
|
||||
nm = hass.data.get(DOMAIN, {}).get(NOTIFICATION_MANAGER_KEY)
|
||||
if nm is not None:
|
||||
nm.clear_task_state(entry.entry_id, task_id)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user