158 files

This commit is contained in:
Home Assistant Version Control
2026-08-11 11:40:58 +00:00
parent c6db6ca558
commit 5388267a3e
158 changed files with 11840 additions and 698 deletions
@@ -133,6 +133,9 @@ SERVICE_COMPLETE_SCHEMA = vol.Schema(
vol.Optional("duration"): vol.All(vol.Coerce(int), vol.Range(min=0, max=MAX_DURATION_MINUTES)),
# Meter readings (v2.20, #83): recorded value for `reading` tasks.
vol.Optional("reading_value"): vol.All(vol.Coerce(float), vol.Range(min=-1e12, max=1e12)),
# #128: who did it — a person ENTITY (validated picker, no free text);
# resolved to the linked HA user id. Omitted -> the calling user.
vol.Optional("completed_by"): cv.entity_id,
}
)
@@ -215,6 +218,10 @@ SERVICE_UPDATE_TASK_SCHEMA = vol.Schema(
vol.Optional("notes"): vol.All(cv.string, vol.Length(max=MAX_TEXT_LENGTH)),
vol.Optional("priority"): vol.In(TASK_PRIORITIES),
vol.Optional("labels"): vol.All([vol.All(cv.string, vol.Length(max=MAX_LABEL_LENGTH))], vol.Length(max=MAX_LABELS)),
# #128: (re)assign via automations — a person ENTITY resolved to the
# linked HA user; the boolean clears the assignment instead.
vol.Optional("responsible_user"): cv.entity_id,
vol.Optional("clear_responsible_user"): cv.boolean,
}
)
@@ -425,6 +432,25 @@ async def _async_setup_shared(hass: HomeAssistant) -> bool:
async_register_document_views(hass)
def _resolve_person_user_id(person_entity_id: str) -> str:
"""Resolve a person ENTITY to its linked HA user id (#128).
Person entities are the validated way to reference a user in a
service form (HA has no user selector) — the picker rules out typos,
and the entity's ``user_id`` attribute carries the account link.
"""
state = hass.states.get(person_entity_id)
if state is None or not person_entity_id.startswith("person."):
raise ServiceValidationError(
f"{person_entity_id!r} is not a known person entity"
)
user_id = state.attributes.get("user_id")
if not user_id:
raise ServiceValidationError(
f"Person {state.name!r} is not linked to a Home Assistant user account"
)
return str(user_id)
async def _handle_complete(call: ServiceCall) -> None:
"""Handle the complete service call."""
entity_id = call.data[ATTR_ENTITY_ID]
@@ -442,12 +468,19 @@ async def _async_setup_shared(hass: HomeAssistant) -> bool:
translation_key="no_task_for_entity",
translation_placeholders={"entity_id": entity_id},
)
# #128: explicit person beats the call context; the context covers the
# common case for free (a dashboard tap propagates the tapping user).
if call.data.get("completed_by"):
completed_by: str | None = _resolve_person_user_id(call.data["completed_by"])
else:
completed_by = call.context.user_id if call.context else None
await coordinator.complete_maintenance(
task_id=task_id,
notes=call.data.get("notes"),
cost=call.data.get("cost"),
duration=call.data.get("duration"),
reading_value=call.data.get("reading_value"),
completed_by=completed_by,
)
async def _handle_reset(call: ServiceCall) -> None:
@@ -599,6 +632,16 @@ async def _async_setup_shared(hass: HomeAssistant) -> bool:
"priority": call.data.get("priority"),
"labels": call.data.get("labels"),
}
# #128: assignment via automations. Person entity -> HA user id; the
# clear flag maps to "" (async_update_task_simple pops the key).
if call.data.get("responsible_user") and call.data.get("clear_responsible_user"):
raise ServiceValidationError(
"Provide either responsible_user or clear_responsible_user, not both"
)
if call.data.get("responsible_user"):
updates["responsible_user_id"] = _resolve_person_user_id(call.data["responsible_user"])
elif call.data.get("clear_responsible_user"):
updates["responsible_user_id"] = ""
try:
await async_update_task_simple(
hass,
@@ -29,10 +29,9 @@ from .helpers.task_fields import INTERVAL_DAYS_RANGE
if TYPE_CHECKING:
from homeassistant.config_entries import ConfigEntry
# UI-only cap for the adaptive MINIMUM interval (one year). Deliberately not a
# shared const: the adaptive min has no WS/schema twin to drift from — the
# engine clamps recommendations to the per-task min/max pair itself.
_ADAPTIVE_MIN_INTERVAL_CAP_DAYS = 365
# Cap for the adaptive MINIMUM interval (one year). Shared with the
# task/set_adaptive WS schema since the parity round — same field, two UIs.
from .const import ADAPTIVE_MIN_INTERVAL_CAP_DAYS as _ADAPTIVE_MIN_INTERVAL_CAP_DAYS
class AdaptiveMixin:
@@ -88,6 +88,46 @@ TRIGGER_ENTITY_DOMAINS = [
]
def _apply_recovery_flag(tc: dict[str, Any], user_input: dict[str, Any]) -> None:
"""Write the #53 auto-complete-on-recovery flag from a form submission.
Editable since the dialog/flow parity round — before that the flow only
carried a previously stored value. Absence in trigger_config means off.
"""
if user_input.get("auto_complete_on_recovery"):
tc["auto_complete_on_recovery"] = True
else:
tc.pop("auto_complete_on_recovery", None)
def _recovery_default(tc: dict[str, Any] | None) -> bool:
return bool((tc or {}).get("auto_complete_on_recovery"))
def _state_selector(entity_id: str | None, *, multiple: bool = False) -> Any:
"""State field bound to the trigger entity (#129 follow-up).
Suggests the entity's known states instead of free text — typo'd states
were a real failure mode. Falls back to a plain text field when no entity
is available (defensive; the entity step always runs first).
"""
if entity_id:
return selector.StateSelector(
selector.StateSelectorConfig(entity_id=entity_id, multiple=multiple)
)
return selector.TextSelector(selector.TextSelectorConfig(type=selector.TextSelectorType.TEXT))
def _parse_states(raw: Any) -> list[str]:
"""Normalize an on-states submission — list from the state selector,
comma string from the legacy text fallback."""
if isinstance(raw, list):
return [s.strip().lower() for s in raw if isinstance(s, str) and s.strip()]
if isinstance(raw, str):
return [s.strip().lower() for s in raw.split(",") if s.strip()]
return []
class TriggerConfigMixin:
"""Shared sensor trigger configuration logic for ConfigFlow and OptionsFlow.
@@ -381,6 +421,7 @@ class TriggerConfigMixin:
if below is not None:
tc[CONF_TRIGGER_BELOW] = below
tc[CONF_TRIGGER_FOR_MINUTES] = user_input.get(CONF_TRIGGER_FOR_MINUTES, 0)
_apply_recovery_flag(tc, user_input)
# Multi-entity: store entity_logic if multiple entities selected
entity_ids = tc.get("entity_ids", [])
@@ -419,6 +460,10 @@ class TriggerConfigMixin:
vol.Optional(CONF_TRIGGER_FOR_MINUTES, default=0): selector.NumberSelector(
selector.NumberSelectorConfig(min=0, max=1440, step=1, mode=selector.NumberSelectorMode.BOX)
),
vol.Optional(
"auto_complete_on_recovery",
default=_recovery_default(self._current_task.get("trigger_config")),
): selector.BooleanSelector(),
}
# Add entity_logic selector when multiple entities are selected
@@ -480,6 +525,13 @@ class TriggerConfigMixin:
tc = self._current_task["trigger_config"]
tc[CONF_TRIGGER_TARGET_VALUE] = user_input[CONF_TRIGGER_TARGET_VALUE]
tc[CONF_TRIGGER_DELTA_MODE] = user_input.get(CONF_TRIGGER_DELTA_MODE, False)
_apply_recovery_flag(tc, user_input)
# Counting start value (#102/#103): editable here since the parity
# round — an omitted field keeps the value the attribute step
# carried over; the backend clears stale Store state on change.
baseline = user_input.get("trigger_baseline_value")
if baseline is not None and baseline >= 0:
tc["trigger_baseline_value"] = baseline
# Multi-entity: store entity_logic if multiple entities selected
entity_ids = tc.get("entity_ids", [])
@@ -510,6 +562,12 @@ class TriggerConfigMixin:
else:
current_value = state.state
prev_tc = self._current_task.get("trigger_config") or {}
baseline_key = (
vol.Optional("trigger_baseline_value", default=prev_tc["trigger_baseline_value"])
if "trigger_baseline_value" in prev_tc
else vol.Optional("trigger_baseline_value")
)
schema_fields: dict[Any, Any] = {
vol.Required(CONF_TRIGGER_TARGET_VALUE): selector.NumberSelector(
selector.NumberSelectorConfig(
@@ -518,6 +576,17 @@ class TriggerConfigMixin:
)
),
vol.Optional(CONF_TRIGGER_DELTA_MODE, default=False): selector.BooleanSelector(),
baseline_key: selector.NumberSelector(
selector.NumberSelectorConfig(
min=0,
mode=selector.NumberSelectorMode.BOX,
step="any",
)
),
vol.Optional(
"auto_complete_on_recovery",
default=_recovery_default(prev_tc),
): selector.BooleanSelector(),
}
# Add entity_logic selector when multiple entities are selected
@@ -590,6 +659,7 @@ class TriggerConfigMixin:
if to_state:
tc[CONF_TRIGGER_TO_STATE] = to_state
tc[CONF_TRIGGER_TARGET_CHANGES] = user_input.get(CONF_TRIGGER_TARGET_CHANGES, 1)
_apply_recovery_flag(tc, user_input)
# Multi-entity: store entity_logic if multiple entities selected
entity_ids = tc.get("entity_ids", [])
@@ -608,12 +678,8 @@ class TriggerConfigMixin:
return on_complete()
schema_fields: dict[Any, Any] = {
vol.Optional(CONF_TRIGGER_FROM_STATE): selector.TextSelector(
selector.TextSelectorConfig(type=selector.TextSelectorType.TEXT)
),
vol.Optional(CONF_TRIGGER_TO_STATE): selector.TextSelector(
selector.TextSelectorConfig(type=selector.TextSelectorType.TEXT)
),
vol.Optional(CONF_TRIGGER_FROM_STATE): _state_selector(self._trigger_entity_id),
vol.Optional(CONF_TRIGGER_TO_STATE): _state_selector(self._trigger_entity_id),
vol.Required(CONF_TRIGGER_TARGET_CHANGES, default=1): selector.NumberSelector(
selector.NumberSelectorConfig(
min=1,
@@ -622,6 +688,10 @@ class TriggerConfigMixin:
mode=selector.NumberSelectorMode.BOX,
)
),
vol.Optional(
"auto_complete_on_recovery",
default=_recovery_default(self._current_task.get("trigger_config")),
): selector.BooleanSelector(),
}
# Add entity_logic selector when multiple entities are selected
@@ -684,12 +754,12 @@ class TriggerConfigMixin:
tc = self._current_task["trigger_config"]
tc[CONF_TRIGGER_RUNTIME_HOURS] = user_input[CONF_TRIGGER_RUNTIME_HOURS]
# Parse comma-separated ON states
raw_states = user_input.get(CONF_TRIGGER_ON_STATES, "")
if raw_states and raw_states.strip():
tc[CONF_TRIGGER_ON_STATES] = [s.strip().lower() for s in raw_states.split(",") if s.strip()]
states = _parse_states(user_input.get(CONF_TRIGGER_ON_STATES))
if states:
tc[CONF_TRIGGER_ON_STATES] = states
else:
tc.pop(CONF_TRIGGER_ON_STATES, None)
_apply_recovery_flag(tc, user_input)
# Multi-entity: store entity_logic if multiple entities selected
entity_ids = tc.get("entity_ids", [])
@@ -709,8 +779,9 @@ class TriggerConfigMixin:
# Pre-fill existing custom states for editing
current_tc = self._current_task.get("trigger_config", {})
existing_states = current_tc.get(CONF_TRIGGER_ON_STATES)
default_states = ", ".join(existing_states) if existing_states else ""
existing_states = current_tc.get(CONF_TRIGGER_ON_STATES) or []
# The state selector takes/returns a LIST; the text fallback a comma string.
default_states: Any = list(existing_states) if self._trigger_entity_id else ", ".join(existing_states)
schema_fields: dict[Any, Any] = {
vol.Required(CONF_TRIGGER_RUNTIME_HOURS): selector.NumberSelector(
@@ -722,11 +793,13 @@ class TriggerConfigMixin:
unit_of_measurement="h",
)
),
vol.Optional(CONF_TRIGGER_ON_STATES, default=default_states): selector.TextSelector(
selector.TextSelectorConfig(
type=selector.TextSelectorType.TEXT,
)
vol.Optional(CONF_TRIGGER_ON_STATES, default=default_states): _state_selector(
self._trigger_entity_id, multiple=True
),
vol.Optional(
"auto_complete_on_recovery",
default=_recovery_default(current_tc),
): selector.BooleanSelector(),
}
# Add entity_logic selector when multiple entities are selected
@@ -791,15 +864,12 @@ class TriggerConfigMixin:
return cancel
logic = user_input.get(CONF_COMPOUND_LOGIC, "AND").upper()
# Carry over the panel-managed recovery flag (#53) across the
# compound rebuild, mirroring the flat-trigger path above.
prev_tc = self._current_task.get("trigger_config") or {}
self._current_task["trigger_config"] = {
"type": TriggerType.COMPOUND,
CONF_COMPOUND_LOGIC: logic,
CONF_COMPOUND_CONDITIONS: [],
**({"auto_complete_on_recovery": True} if prev_tc.get("auto_complete_on_recovery") else {}),
}
_apply_recovery_flag(self._current_task["trigger_config"], user_input)
if not hasattr(self, "_compound_conditions"):
self._compound_conditions: list[dict[str, Any]] = []
self._compound_conditions = []
@@ -823,6 +893,10 @@ class TriggerConfigMixin:
translation_key="compound_logic",
)
),
vol.Optional(
"auto_complete_on_recovery",
default=_recovery_default(self._current_task.get("trigger_config")),
): selector.BooleanSelector(),
}
return self.async_show_form(
step_id=step_id,
@@ -961,9 +1035,9 @@ class TriggerConfigMixin:
cond["trigger_target_changes"] = user_input.get(CONF_TRIGGER_TARGET_CHANGES, 1)
elif condition_type == TriggerType.RUNTIME:
cond["trigger_runtime_hours"] = user_input[CONF_TRIGGER_RUNTIME_HOURS]
raw_states = user_input.get(CONF_TRIGGER_ON_STATES, "")
if raw_states and raw_states.strip():
cond["trigger_on_states"] = [s.strip().lower() for s in raw_states.split(",") if s.strip()]
states = _parse_states(user_input.get(CONF_TRIGGER_ON_STATES))
if states:
cond["trigger_on_states"] = states
entity_ids = cond.get("entity_ids", [])
if len(entity_ids) > 1 and user_input.get(CONF_TRIGGER_ENTITY_LOGIC):
@@ -1000,13 +1074,10 @@ class TriggerConfigMixin:
vol.Optional(CONF_TRIGGER_DELTA_MODE, default=False): selector.BooleanSelector(),
}
elif condition_type == TriggerType.STATE_CHANGE:
cond_entity = cond.get("entity_id")
schema_fields = {
vol.Optional(CONF_TRIGGER_FROM_STATE): selector.TextSelector(
selector.TextSelectorConfig(type=selector.TextSelectorType.TEXT)
),
vol.Optional(CONF_TRIGGER_TO_STATE): selector.TextSelector(
selector.TextSelectorConfig(type=selector.TextSelectorType.TEXT)
),
vol.Optional(CONF_TRIGGER_FROM_STATE): _state_selector(cond_entity),
vol.Optional(CONF_TRIGGER_TO_STATE): _state_selector(cond_entity),
vol.Required(CONF_TRIGGER_TARGET_CHANGES, default=1): selector.NumberSelector(
selector.NumberSelectorConfig(
min=1,
@@ -1027,9 +1098,10 @@ class TriggerConfigMixin:
unit_of_measurement="h",
)
),
vol.Optional(CONF_TRIGGER_ON_STATES, default=""): selector.TextSelector(
selector.TextSelectorConfig(type=selector.TextSelectorType.TEXT)
),
vol.Optional(
CONF_TRIGGER_ON_STATES,
default=[] if cond.get("entity_id") else "",
): _state_selector(cond.get("entity_id"), multiple=True),
}
entity_ids = cond.get("entity_ids", [])
@@ -403,6 +403,9 @@ CONF_ADAPTIVE_ENABLED = "adaptive_enabled"
CONF_ADAPTIVE_EWA_ALPHA = "ewa_alpha"
CONF_ADAPTIVE_MIN_INTERVAL = "min_interval_days"
CONF_ADAPTIVE_MAX_INTERVAL = "max_interval_days"
# Deliberate cap: a MINIMUM interval above a year defeats adaptive learning.
# Shared by the options flow's adaptive step and the task/set_adaptive WS.
ADAPTIVE_MIN_INTERVAL_CAP_DAYS = 365
# --- Config Keys: Seasonal Scheduling ---
CONF_SEASONAL_ENABLED = "seasonal_enabled"
@@ -33,9 +33,11 @@ function completeDialog(el: HTMLElement): MaintenanceCompleteDialog | null {
}
/** The dialogs are lazy code-split chunks — the open lands whenever the
* whole lazy-UI group has loaded, so poll instead of guessing a delay. */
* whole lazy-UI group has loaded, so poll instead of guessing a delay.
* Generous window: under full-suite concurrency (70+ files) the 2 s the
* poll originally allowed was load-dependent flaky. */
async function waitForOpenCompleteDialog(el: HTMLElement): Promise<void> {
for (let i = 0; i < 100; i++) {
for (let i = 0; i < 400; i++) {
if (completeDialog(el)?.shadowRoot?.querySelector("ha-dialog")) return;
await new Promise((r) => setTimeout(r, 20));
}
@@ -6,6 +6,11 @@ import type { AdaptiveConfig, HomeAssistant, MaintenanceTask, TaskPartLink, Trig
import { formatDate, t, weekdayName } from "../styles";
import { UserService } from "../user-service";
import { partLinkKey } from "../helpers/shared-parts";
import {
ENVIRONMENTAL_PICKER_DEVICE_CLASSES,
ENVIRONMENTAL_PICKER_DOMAINS,
TRIGGER_PICKER_DOMAINS,
} from "../helpers/trigger-domains";
import { describeWsError } from "../ws-errors";
import { REQUIRED_COMPLETION_KEYS, REQUIRED_COMPLETION_LABELS } from "./required-completion-labels";
@@ -25,6 +30,7 @@ const TRIGGER_TYPE_KEYS_WITH_COMPOUND = [...TRIGGER_TYPE_KEYS, "compound"];
interface CompoundConditionDraft {
entityIds: string; // comma-separated raw input
type: string; // threshold | counter | state_change | runtime
attribute: string; // "" = use the entity state
above: string;
below: string;
forMinutes: string;
@@ -42,7 +48,7 @@ interface CompoundConditionDraft {
function emptyCondition(): CompoundConditionDraft {
return {
entityIds: "", type: "threshold", above: "", below: "", forMinutes: "0",
entityIds: "", type: "threshold", attribute: "", above: "", below: "", forMinutes: "0",
targetValue: "", deltaMode: false, fromState: "", toState: "",
targetChanges: "", runtimeHours: "", onStates: "", carry: {},
};
@@ -51,7 +57,7 @@ function emptyCondition(): CompoundConditionDraft {
/** Keys the compound editor owns via its own form fields — everything else
* travels through `carry` untouched. */
const MANAGED_CONDITION_KEYS = new Set([
"entity_id", "entity_ids", "type",
"entity_id", "entity_ids", "type", "attribute",
"trigger_above", "trigger_below", "trigger_for_minutes",
"trigger_target_value", "trigger_delta_mode",
"trigger_from_state", "trigger_to_state", "trigger_target_changes",
@@ -64,6 +70,7 @@ function conditionToDraft(c: TriggerConfig): CompoundConditionDraft {
return {
entityIds: ids.join(", "),
type: c.type || "threshold",
attribute: c.attribute || "",
above: c.trigger_above?.toString() ?? "",
below: c.trigger_below?.toString() ?? "",
forMinutes: c.trigger_for_minutes?.toString() ?? "0",
@@ -86,6 +93,7 @@ function draftToCondition(d: CompoundConditionDraft): TriggerConfig | null {
const ids = d.entityIds.split(",").map((s) => s.trim()).filter(Boolean);
if (ids.length === 0) return null;
const c: TriggerConfig = { ...(d.carry || {}), entity_id: ids[0], entity_ids: ids, type: d.type };
if (d.attribute) c.attribute = d.attribute;
if (d.type === "threshold") {
const a = parseFloat(d.above); if (!isNaN(a)) c.trigger_above = a;
const b = parseFloat(d.below); if (!isNaN(b)) c.trigger_below = b;
@@ -133,6 +141,11 @@ export class MaintenanceTaskDialog extends LitElement {
parts: Array<{ id: string; name: string; unit?: string }>;
}> = [];
@state() private _open = false;
// #129: flips the trigger entity pickers back to comma text fields when the
// HA picker fails to lay out in this mount context (see _probeEntityPickers).
@state() private _entityPickerFallback = false;
private _pickerProbeTimer: ReturnType<typeof setTimeout> | undefined;
private _pickerProbeStrikes = 0;
@state() private _loading = false;
@state() private _error = "";
@state() private _entryId = "";
@@ -263,6 +276,22 @@ export class MaintenanceTaskDialog extends LitElement {
@state() private _environmentalAttribute = "";
private _environmentalInitial = ""; // for change detection on save
private _environmentalAttributeInitial = "";
// Adaptive tuning (parity with the options flow's adaptive step) — Store-
// managed like the environmental binding, saved through task/set_adaptive.
@state() private _adaptiveEnabled = false;
@state() private _adaptiveAlpha = "0.3";
@state() private _adaptiveMin = "7";
@state() private _adaptiveMax = "365";
@state() private _adaptiveSeasonal = true;
@state() private _adaptivePrediction = true;
private _adaptiveInitial = "";
private _adaptiveSnapshot(): string {
return JSON.stringify([
this._adaptiveEnabled, this._adaptiveAlpha, this._adaptiveMin,
this._adaptiveMax, this._adaptiveSeasonal, this._adaptivePrediction,
]);
}
private _userService: UserService | null = null;
private get _lang(): string {
@@ -374,6 +403,13 @@ export class MaintenanceTaskDialog extends LitElement {
this._environmentalAttribute = ac.environmental_attribute || "";
this._environmentalInitial = this._environmentalEntity;
this._environmentalAttributeInitial = this._environmentalAttribute;
this._adaptiveEnabled = !!ac.enabled;
this._adaptiveAlpha = (ac.ewa_alpha ?? 0.3).toString();
this._adaptiveMin = (ac.min_interval_days ?? 7).toString();
this._adaptiveMax = (ac.max_interval_days ?? 365).toString();
this._adaptiveSeasonal = ac.seasonal_enabled !== false;
this._adaptivePrediction = ac.sensor_prediction_enabled !== false;
this._adaptiveInitial = this._adaptiveSnapshot();
if (task.trigger_config) {
const tc = task.trigger_config;
@@ -460,6 +496,13 @@ export class MaintenanceTaskDialog extends LitElement {
this._environmentalAttribute = "";
this._environmentalInitial = "";
this._environmentalAttributeInitial = "";
this._adaptiveEnabled = false;
this._adaptiveAlpha = "0.3";
this._adaptiveMin = "7";
this._adaptiveMax = "365";
this._adaptiveSeasonal = true;
this._adaptivePrediction = true;
this._adaptiveInitial = this._adaptiveSnapshot();
// v1.3.0
this._actionService = "";
this._actionTargetEntity = "";
@@ -802,6 +845,46 @@ export class MaintenanceTaskDialog extends LitElement {
}
}
/** Per-entity attribute options — generic entity_id-keyed cache, fetched
* lazily; serves the compound condition rows AND the environmental
* attribute dropdown. (Parity round: the flow's compound path always had
* an attribute step; the dialog only carried it without an editor.) */
@state() private _conditionAttrOptions: Record<
string,
{ suggested: string[]; available: Array<{ name: string; numeric: boolean }> }
> = {};
private _conditionAttrPending = new Set<string>();
private _fetchConditionAttributes(entityId: string): void {
if (!entityId || !this.hass) return;
if (this._conditionAttrOptions[entityId] || this._conditionAttrPending.has(entityId)) return;
this._conditionAttrPending.add(entityId);
void this.hass.connection
.sendMessagePromise({
type: "maintenance_supporter/entity/attributes",
entity_id: entityId,
})
.then((result) => {
const r = result as {
suggested_attributes: string[];
available_attributes: Array<{ name: string; numeric: boolean }>;
};
this._conditionAttrOptions = {
...this._conditionAttrOptions,
[entityId]: {
suggested: r.suggested_attributes || [],
available: r.available_attributes || [],
},
};
})
.catch(() => {
this._conditionAttrOptions = {
...this._conditionAttrOptions,
[entityId]: { suggested: [], available: [] },
};
});
}
private async _fetchEntityAttributes(entityId: string): Promise<void> {
if (!entityId || !this.hass) {
this._suggestedAttributes = [];
@@ -894,6 +977,14 @@ export class MaintenanceTaskDialog extends LitElement {
private async _save(): Promise<void> {
if (this._loading) return; // synchronous re-entry guard (double-click)
if (!this._name.trim()) return;
if (this._adaptiveSnapshot() !== this._adaptiveInitial) {
const minIv = parseInt(this._adaptiveMin, 10);
const maxIv = parseInt(this._adaptiveMax, 10);
if (!isNaN(minIv) && !isNaN(maxIv) && minIv > maxIv) {
this._error = `${t("adaptive_min_interval", this._lang)} > ${t("adaptive_max_interval", this._lang)}`;
return;
}
}
this._loading = true;
this._error = "";
try {
@@ -1116,6 +1207,30 @@ export class MaintenanceTaskDialog extends LitElement {
}
}
// Adaptive tuning is Store-managed like the environmental binding —
// dedicated endpoint, only called when something actually changed.
if (savedTaskId && this._adaptiveSnapshot() !== this._adaptiveInitial) {
const alpha = parseFloat(this._adaptiveAlpha);
const minIv = parseInt(this._adaptiveMin, 10);
const maxIv = parseInt(this._adaptiveMax, 10);
try {
await this.hass.connection.sendMessagePromise({
type: "maintenance_supporter/task/set_adaptive",
entry_id: this._entryId,
task_id: savedTaskId,
enabled: this._adaptiveEnabled,
...(alpha >= 0.1 && alpha <= 0.9 ? { ewa_alpha: alpha } : {}),
...(!isNaN(minIv) && minIv >= 1 ? { min_interval_days: minIv } : {}),
...(!isNaN(maxIv) && maxIv >= 1 ? { max_interval_days: maxIv } : {}),
seasonal_enabled: this._adaptiveSeasonal,
sensor_prediction_enabled: this._adaptivePrediction,
});
this._adaptiveInitial = this._adaptiveSnapshot();
} catch {
/* non-fatal — task itself saved */
}
}
this._open = false;
this.dispatchEvent(new CustomEvent("task-saved"));
} catch (e) {
@@ -1127,6 +1242,11 @@ export class MaintenanceTaskDialog extends LitElement {
private _close(): void {
this._open = false;
if (this._pickerProbeTimer !== undefined) {
clearTimeout(this._pickerProbeTimer);
this._pickerProbeTimer = undefined;
}
this._pickerProbeStrikes = 0;
}
private _renderTriggerFields() {
@@ -1148,17 +1268,40 @@ export class MaintenanceTaskDialog extends LitElement {
</select>
</div>
${isCompound ? this._renderCompoundEditor() : html`
<ms-textfield
label="${t("entity_id", L)} (${t("comma_separated", L)})"
.value=${this._triggerEntityIds.length > 0 ? this._triggerEntityIds.join(", ") : this._triggerEntityId}
@input=${(e: Event) => {
const raw = (e.target as HTMLInputElement).value;
const ids = raw.split(",").map((s: string) => s.trim()).filter(Boolean);
${this._entityPickerFallback ? html`
<ms-textfield
label="${t("entity_id", L)} (${t("comma_separated", L)})"
.value=${this._triggerEntityIds.length > 0 ? this._triggerEntityIds.join(", ") : this._triggerEntityId}
@input=${(e: Event) => {
const raw = (e.target as HTMLInputElement).value;
const ids = raw.split(",").map((s: string) => s.trim()).filter(Boolean);
this._triggerEntityId = ids[0] || "";
this._triggerEntityIds = ids;
if (ids[0]) this._fetchEntityAttributes(ids[0]);
}}
></ms-textfield>
` : html`
<ha-form
class="entity-picker-form"
.hass=${this.hass}
.schema=${[{
name: "trigger_entities",
selector: { entity: { multiple: true, domain: TRIGGER_PICKER_DOMAINS } },
}]}
.data=${{
trigger_entities: this._triggerEntityIds.length > 0
? this._triggerEntityIds
: this._triggerEntityId ? [this._triggerEntityId] : [],
}}
.computeLabel=${() => t("entity_id", L)}
@value-changed=${(e: CustomEvent) => {
const ids = ((e.detail.value as { trigger_entities?: string[] }).trigger_entities || []).filter(Boolean);
this._triggerEntityId = ids[0] || "";
this._triggerEntityIds = ids;
if (ids[0]) this._fetchEntityAttributes(ids[0]);
else this._fetchEntityAttributes("");
}}
></ms-textfield>
></ha-form>`}
${this._triggerEntityIds.length > 1 ? html`
<div class="select-row">
<label>${t("entity_logic", L)}</label>
@@ -1275,11 +1418,28 @@ export class MaintenanceTaskDialog extends LitElement {
@click=${() => this._removeCondition(i)}
>✕</button>
</div>
<ms-textfield
label="${t("entity_id", L)} (${t("comma_separated", L)})"
.value=${c.entityIds}
@input=${(e: Event) => this._patchCondition(i, { entityIds: (e.target as HTMLInputElement).value })}
></ms-textfield>
${this._entityPickerFallback ? html`
<ms-textfield
label="${t("entity_id", L)} (${t("comma_separated", L)})"
.value=${c.entityIds}
@input=${(e: Event) => this._patchCondition(i, { entityIds: (e.target as HTMLInputElement).value })}
></ms-textfield>
` : html`
<ha-form
class="entity-picker-form"
.hass=${this.hass}
.schema=${[{
name: "condition_entities",
selector: { entity: { multiple: true, domain: TRIGGER_PICKER_DOMAINS } },
}]}
.data=${{ condition_entities: c.entityIds.split(",").map((s) => s.trim()).filter(Boolean) }}
.computeLabel=${() => t("entity_id", L)}
@value-changed=${(e: CustomEvent) => {
const ids = ((e.detail.value as { condition_entities?: string[] }).condition_entities || []).filter(Boolean);
this._patchCondition(i, { entityIds: ids.join(", ") });
}}
></ha-form>`}
${this._renderConditionAttribute(c, i)}
<div class="select-row">
<label>${t("trigger_type", L)}</label>
<select
@@ -1296,6 +1456,198 @@ export class MaintenanceTaskDialog extends LitElement {
`;
}
/** State field bound to an entity (#129 follow-up): HA's state selector
* suggests the entity's known states instead of free text. Falls back to
* the plain textfield without an entity or when the pickers are broken in
* this context (same _entityPickerFallback flag). */
private _renderStateField(args: {
label: string;
value: string;
entityId: string;
onInput: (v: string) => void;
}) {
if (this._entityPickerFallback || !args.entityId) {
return html`
<ms-textfield
label=${args.label}
.value=${args.value}
@input=${(e: Event) => args.onInput((e.target as HTMLInputElement).value)}
></ms-textfield>
`;
}
return html`
<ha-form
class="state-picker-form"
.hass=${this.hass}
.schema=${[{ name: "s", selector: { state: { entity_id: args.entityId } } }]}
.data=${{ s: args.value }}
.computeLabel=${() => args.label}
@value-changed=${(e: CustomEvent) =>
args.onInput(((e.detail.value as { s?: string }).s || "").trim())}
></ha-form>
`;
}
/** Multi-state variant for runtime ON-states — keeps the internal
* comma-string representation so the save path stays unchanged. */
private _renderOnStatesField(args: { value: string; entityId: string; onInput: (v: string) => void }) {
const L = this._lang;
if (this._entityPickerFallback || !args.entityId) {
return html`
<ms-textfield
label="${t("runtime_on_states", L)}"
placeholder="on"
.value=${args.value}
@input=${(e: Event) => args.onInput((e.target as HTMLInputElement).value)}
></ms-textfield>
`;
}
return html`
<ha-form
class="state-picker-form"
.hass=${this.hass}
.schema=${[{ name: "s", selector: { state: { entity_id: args.entityId, multiple: true } } }]}
.data=${{ s: (args.value || "").split(",").map((x) => x.trim()).filter(Boolean) }}
.computeLabel=${() => t("runtime_on_states", L)}
@value-changed=${(e: CustomEvent) =>
args.onInput((((e.detail.value as { s?: string[] }).s) || []).join(", "))}
></ha-form>
`;
}
/** Adaptive-scheduling tuning (parity with the options flow's adaptive
* step). Hidden for one-time/manual tasks — there is no recurrence to
* adapt. Collapsed unless adaptive is already enabled. */
private _renderAdaptiveSection(L: string) {
if (this._scheduleType === "one_time" || this._scheduleType === "manual") return nothing;
return html`
<details class="adaptive-section" ?open=${this._adaptiveEnabled}>
<summary>${t("adaptive_section_title", L)}</summary>
<label>
<input
type="checkbox"
.checked=${this._adaptiveEnabled}
@change=${(e: Event) => (this._adaptiveEnabled = (e.target as HTMLInputElement).checked)}
/>
${t("adaptive_enabled", L)}
</label>
${this._adaptiveEnabled ? html`
<ms-textfield
label="${t("adaptive_min_interval", L)}"
type="number"
min="1"
.value=${this._adaptiveMin}
@input=${(e: Event) => (this._adaptiveMin = (e.target as HTMLInputElement).value)}
></ms-textfield>
<ms-textfield
label="${t("adaptive_max_interval", L)}"
type="number"
min="1"
.value=${this._adaptiveMax}
@input=${(e: Event) => (this._adaptiveMax = (e.target as HTMLInputElement).value)}
></ms-textfield>
<ms-textfield
label="${t("adaptive_ewa_alpha", L)}"
type="number"
min="0.1"
max="0.9"
step="0.1"
.value=${this._adaptiveAlpha}
@input=${(e: Event) => (this._adaptiveAlpha = (e.target as HTMLInputElement).value)}
></ms-textfield>
<label>
<input
type="checkbox"
.checked=${this._adaptiveSeasonal}
@change=${(e: Event) => (this._adaptiveSeasonal = (e.target as HTMLInputElement).checked)}
/>
${t("adaptive_seasonal_enabled", L)}
</label>
<label>
<input
type="checkbox"
.checked=${this._adaptivePrediction}
@change=${(e: Event) => (this._adaptivePrediction = (e.target as HTMLInputElement).checked)}
/>
${t("adaptive_prediction_enabled", L)}
</label>
` : nothing}
</details>
`;
}
/** Environmental attribute — the same live-fetched dropdown the flat and
* compound attribute fields use, keyed by the environmental entity. */
private _renderEnvironmentalAttribute(L: string) {
this._fetchConditionAttributes(this._environmentalEntity);
const opts = this._conditionAttrOptions[this._environmentalEntity];
if (opts && opts.available.length > 0) {
return html`
<div class="select-row">
<label>${t("environmental_attribute_optional", L)}</label>
<select
.value=${this._environmentalAttribute}
@change=${(e: Event) => (this._environmentalAttribute = (e.target as HTMLSelectElement).value)}
>
<option value="" ?selected=${!this._environmentalAttribute}>${t("use_entity_state", L)}</option>
${opts.suggested.map(
(attr) => html`<option value=${attr} ?selected=${attr === this._environmentalAttribute}>${attr} ★</option>`
)}
${opts.available
.filter((a) => !opts.suggested.includes(a.name))
.map(
(a) => html`<option value=${a.name} ?selected=${a.name === this._environmentalAttribute}>${a.name}${a.numeric ? "" : " (non-numeric)"}</option>`
)}
</select>
</div>
`;
}
return html`
<ms-textfield
label="${t("environmental_attribute_optional", L)}"
.value=${this._environmentalAttribute}
@input=${(e: Event) => (this._environmentalAttribute = (e.target as HTMLInputElement).value.trim())}
></ms-textfield>
`;
}
/** Attribute selector for one compound condition — the same live-fetched
* dropdown the flat editor has, keyed by the condition's first entity. */
private _renderConditionAttribute(c: CompoundConditionDraft, i: number) {
const L = this._lang;
const firstId = c.entityIds.split(",")[0]?.trim() || "";
if (firstId) this._fetchConditionAttributes(firstId);
const opts = firstId ? this._conditionAttrOptions[firstId] : undefined;
if (opts && opts.available.length > 0) {
return html`
<div class="select-row">
<label>${t("attribute_optional", L)}</label>
<select
.value=${c.attribute}
@change=${(e: Event) => this._patchCondition(i, { attribute: (e.target as HTMLSelectElement).value })}
>
<option value="" ?selected=${!c.attribute}>${t("use_entity_state", L)}</option>
${opts.suggested.map(
(attr) => html`<option value=${attr} ?selected=${attr === c.attribute}>${attr} ★</option>`
)}
${opts.available
.filter((a) => !opts.suggested.includes(a.name))
.map(
(a) => html`<option value=${a.name} ?selected=${a.name === c.attribute}>${a.name}${a.numeric ? "" : " (non-numeric)"}</option>`
)}
</select>
</div>
`;
}
return html`
<ms-textfield
label="${t("attribute_optional", L)}"
.value=${c.attribute}
@input=${(e: Event) => this._patchCondition(i, { attribute: (e.target as HTMLInputElement).value.trim() })}
></ms-textfield>
`;
}
/** Type-specific inputs for a single compound condition (mirrors the flat
* per-type fields, bound to the condition draft). */
private _renderConditionTypeFields(c: CompoundConditionDraft, i: number) {
@@ -1322,21 +1674,34 @@ export class MaintenanceTaskDialog extends LitElement {
`;
}
if (c.type === "state_change") {
const condEntity = c.entityIds.split(",")[0]?.trim() || "";
return html`
<ms-textfield label="${t("from_state_optional", L)}" .value=${c.fromState}
@input=${(e: Event) => this._patchCondition(i, { fromState: (e.target as HTMLInputElement).value })}></ms-textfield>
<ms-textfield label="${t("to_state_optional", L)}" .value=${c.toState}
@input=${(e: Event) => this._patchCondition(i, { toState: (e.target as HTMLInputElement).value })}></ms-textfield>
${this._renderStateField({
label: t("from_state_optional", L),
value: c.fromState,
entityId: condEntity,
onInput: (v) => this._patchCondition(i, { fromState: v }),
})}
${this._renderStateField({
label: t("to_state_optional", L),
value: c.toState,
entityId: condEntity,
onInput: (v) => this._patchCondition(i, { toState: v }),
})}
<ms-textfield label="${t("target_changes", L)}" type="number" .value=${c.targetChanges}
@input=${(e: Event) => this._patchCondition(i, { targetChanges: (e.target as HTMLInputElement).value })}></ms-textfield>
`;
}
if (c.type === "runtime") {
const condEntity = c.entityIds.split(",")[0]?.trim() || "";
return html`
<ms-textfield label="${t("runtime_hours", L)}" type="number" .value=${c.runtimeHours}
@input=${(e: Event) => this._patchCondition(i, { runtimeHours: (e.target as HTMLInputElement).value })}></ms-textfield>
<ms-textfield label="${t("runtime_on_states", L)}" placeholder="on" .value=${c.onStates}
@input=${(e: Event) => this._patchCondition(i, { onStates: (e.target as HTMLInputElement).value })}></ms-textfield>
${this._renderOnStatesField({
value: c.onStates,
entityId: condEntity,
onInput: (v) => this._patchCondition(i, { onStates: v }),
})}
`;
}
return nothing;
@@ -1396,6 +1761,7 @@ export class MaintenanceTaskDialog extends LitElement {
protected updated(changed: Map<PropertyKey, unknown>): void {
super.updated?.(changed);
this._scheduleEntityPickerProbe();
for (const key of changed.keys()) {
if (MaintenanceTaskDialog._PREVIEW_RELEVANT.has(String(key))) {
this._schedulePreviewRefresh();
@@ -1404,6 +1770,62 @@ export class MaintenanceTaskDialog extends LitElement {
}
}
/** #129 SAFETY NET (not the primary fix): HA's modern pickers resolve data
* via Lit context — events that must bubble up to providers on the
* <home-assistant> element. A dialog mounted outside that tree gets
* pickers that upgrade to an EMPTY shadow root. The root cause is solved
* by mounting dialogs inside <home-assistant>'s shadow root
* (dialog-mount.ts); this probe remains as defense in depth for unknown
* contexts: two consecutive zero-height measurements of the leaf pickers
* inside a visible dialog flip the trigger fields back to the
* comma-separated text inputs. */
private _scheduleEntityPickerProbe(): void {
if (
this._entityPickerFallback
|| this._pickerProbeTimer !== undefined
|| !this._open
|| this._scheduleType !== "sensor_based"
) return;
this._pickerProbeTimer = setTimeout(() => this._probeEntityPickers(), 1500);
}
private _probeEntityPickers(): void {
this._pickerProbeTimer = undefined;
if (this._entityPickerFallback || !this._open) return;
const form = this.shadowRoot?.querySelector<HTMLElement>("ha-form.entity-picker-form");
const dialogVisible = (this.shadowRoot?.querySelector<HTMLElement>(".content")?.offsetHeight ?? 0) > 0;
if (!form || !dialogVisible) {
this._pickerProbeStrikes = 0;
return;
}
// The broken-context signature is subtle: the form (and even a
// ha-entities-picker wrapper) may keep its label height while the
// ha-entity-picker LEAVES upgrade to empty shadow roots — so collect the
// leaf pickers across ALL picker forms and require every one to lay out.
const collectLeaves = (el: Element | null, out: HTMLElement[], depth = 0): void => {
if (!el || depth > 10) return;
if ((el.tagName?.toLowerCase() ?? "") === "ha-entity-picker") out.push(el as HTMLElement);
for (const root of [el.shadowRoot, el]) {
if (!root) continue;
for (const child of Array.from(root.children ?? [])) collectLeaves(child, out, depth + 1);
}
};
const forms = [...(this.shadowRoot?.querySelectorAll<HTMLElement>("ha-form.entity-picker-form") ?? [])];
const leaves: HTMLElement[] = [];
for (const f of forms) collectLeaves(f, leaves);
const broken = leaves.length === 0 || leaves.some((leaf) => leaf.offsetHeight === 0);
if (form.offsetHeight === 0 || broken) {
this._pickerProbeStrikes += 1;
if (this._pickerProbeStrikes >= 2) {
this._entityPickerFallback = true;
return;
}
this._pickerProbeTimer = setTimeout(() => this._probeEntityPickers(), 700);
} else {
this._pickerProbeStrikes = 0;
}
}
private _schedulePreviewRefresh(): void {
if (this._previewTimer) clearTimeout(this._previewTimer);
this._previewTimer = setTimeout(() => void this._fetchSchedulePreview(), 300);
@@ -1773,17 +2195,19 @@ export class MaintenanceTaskDialog extends LitElement {
}
if (this._triggerType === "state_change") {
return html`
<ms-textfield
label="${t("from_state_optional", L)}"
.value=${this._triggerFromState}
@input=${(e: Event) => (this._triggerFromState = (e.target as HTMLInputElement).value)}
></ms-textfield>
${this._renderStateField({
label: t("from_state_optional", L),
value: this._triggerFromState,
entityId: this._triggerEntityId,
onInput: (v) => (this._triggerFromState = v),
})}
<div class="field-help">${t("state_value_help", L)}</div>
<ms-textfield
label="${t("to_state_optional", L)}"
.value=${this._triggerToState}
@input=${(e: Event) => (this._triggerToState = (e.target as HTMLInputElement).value)}
></ms-textfield>
${this._renderStateField({
label: t("to_state_optional", L),
value: this._triggerToState,
entityId: this._triggerEntityId,
onInput: (v) => (this._triggerToState = v),
})}
<ms-textfield
label="${t("target_changes", L)}"
type="number"
@@ -1803,12 +2227,11 @@ export class MaintenanceTaskDialog extends LitElement {
.value=${this._triggerRuntimeHours}
@input=${(e: Event) => (this._triggerRuntimeHours = (e.target as HTMLInputElement).value)}
></ms-textfield>
<ms-textfield
label="${t("runtime_on_states", L)}"
placeholder="on"
.value=${this._triggerOnStates}
@input=${(e: Event) => (this._triggerOnStates = (e.target as HTMLInputElement).value)}
></ms-textfield>
${this._renderOnStatesField({
value: this._triggerOnStates,
entityId: this._triggerEntityId,
onInput: (v) => (this._triggerOnStates = v),
})}
<div class="field-help">${t("runtime_on_states_help", L)}</div>
`;
}
@@ -2064,20 +2487,34 @@ export class MaintenanceTaskDialog extends LitElement {
` : nothing}
${this._renderTriggerFields()}
${this._scheduleType === "sensor_based" ? html`
<ms-textfield
label="${t("environmental_entity_optional", L)}"
helper="${t("environmental_entity_helper", L)}"
.value=${this._environmentalEntity}
@input=${(e: Event) => (this._environmentalEntity = (e.target as HTMLInputElement).value.trim())}
></ms-textfield>
${this._environmentalEntity ? html`
${this._entityPickerFallback ? html`
<ms-textfield
label="${t("environmental_attribute_optional", L)}"
.value=${this._environmentalAttribute}
@input=${(e: Event) => (this._environmentalAttribute = (e.target as HTMLInputElement).value.trim())}
label="${t("environmental_entity_optional", L)}"
helper="${t("environmental_entity_helper", L)}"
.value=${this._environmentalEntity}
@input=${(e: Event) => (this._environmentalEntity = (e.target as HTMLInputElement).value.trim())}
></ms-textfield>
` : nothing}
` : html`
<ha-form
class="entity-picker-form"
.hass=${this.hass}
.schema=${[{
name: "environmental_entity",
selector: { entity: {
domain: ENVIRONMENTAL_PICKER_DOMAINS,
device_class: ENVIRONMENTAL_PICKER_DEVICE_CLASSES,
} },
}]}
.data=${{ environmental_entity: this._environmentalEntity }}
.computeLabel=${() => t("environmental_entity_optional", L)}
.computeHelper=${() => t("environmental_entity_helper", L)}
@value-changed=${(e: CustomEvent) => {
this._environmentalEntity = ((e.detail.value as { environmental_entity?: string }).environmental_entity || "").trim();
}}
></ha-form>`}
${this._environmentalEntity ? this._renderEnvironmentalAttribute(L) : nothing}
` : nothing}
${this._renderAdaptiveSection(L)}
<ms-textfield
label="${t("notes_optional", L)}"
.value=${this._notes}
@@ -2157,17 +2594,35 @@ export class MaintenanceTaskDialog extends LitElement {
font-weight: 500;
padding-bottom: 12px;
}
/* v1.3.0: completion-action sections */
.ca-section {
/* #129: entity/state pickers in the trigger form (ha-form + selector) */
.entity-picker-form,
.state-picker-form {
display: block;
margin: 8px 0;
}
/* v1.3.0: completion-action sections (.adaptive-section shares the shell
but keeps its own class — tests count .ca-section elements) */
.ca-section,
.adaptive-section {
border: 1px solid var(--divider-color);
border-radius: 6px;
padding: 8px 12px;
margin-top: 8px;
}
.ca-section > summary {
.ca-section > summary,
.adaptive-section > summary {
cursor: pointer;
font-weight: 500;
}
.adaptive-section ms-textfield {
width: 100%;
margin-top: 8px;
display: block;
}
.adaptive-section label {
display: block;
margin-top: 8px;
}
.ca-section ms-textfield,
.ca-section ha-entity-picker,
.ca-section ha-service-picker,
@@ -1,8 +1,17 @@
/** Standalone dialog mounting helper.
*
* Mounts the existing MaintenanceObjectDialog / MaintenanceTaskDialog onto
* document.body so they can be opened from any Lovelace context — without
* the user navigating to the panel first.
* Mounts the existing MaintenanceObjectDialog / MaintenanceTaskDialog into
* <home-assistant>'s shadow root — the same place HA's own dialogs live —
* so they can be opened from any Lovelace context without the user
* navigating to the panel first.
*
* The mount point matters (#129): HA's modern components (ha-entity-picker
* and friends) resolve data through Lit context — `context-request` events
* that bubble UP the DOM to providers on the <home-assistant> element. A
* dialog on document.body is a SIBLING tree of <home-assistant>, the events
* never reach the providers, and such components upgrade to an empty shadow
* root. Mounting inside <home-assistant>'s shadow root keeps the provider
* chain intact; document.body remains only as a last-resort fallback.
*
* Usage from a strategy or card click handler:
*
@@ -54,11 +63,21 @@ function getHass(): HomeAssistant | undefined {
return root?.hass;
}
/** Where dialogs live: <home-assistant>'s shadow root (context providers
* reachable), falling back to document.body if HA's root ever goes away. */
function dialogHost(): ShadowRoot | HTMLElement {
return document.querySelector("home-assistant")?.shadowRoot ?? document.body;
}
function getOrCreate<T extends HTMLElement>(tag: string): T {
let el = document.body.querySelector<T>(tag);
const host = dialogHost();
let el = host.querySelector<T>(tag) ?? document.body.querySelector<T>(tag);
if (!el) {
el = document.createElement(tag) as T;
document.body.appendChild(el);
host.appendChild(el);
} else if (el.parentNode !== host) {
// Adopt a dialog mounted by an older bundle onto document.body.
host.appendChild(el);
}
return el;
}
@@ -205,6 +205,13 @@
"nfc_tags_refresh": "Obnovit",
"environmental_entity_optional": "Senzor prostředí (volitelný)",
"environmental_entity_helper": "např. sensor.outdoor_temperature — upravuje interval podle podmínek prostředí",
"adaptive_prediction_enabled": "Povolit predikce řízené senzory",
"adaptive_seasonal_enabled": "Povolit sezónní povědomí",
"adaptive_max_interval": "Maximální interval (dny)",
"adaptive_min_interval": "Minimální interval (dny)",
"adaptive_ewa_alpha": "Rychlost učení (alpha)",
"adaptive_enabled": "Povolit adaptivní plánování",
"adaptive_section_title": "Adaptivní plánování",
"environmental_attribute_optional": "Atribut prostředí (volitelný)",
"nfc_tag_id": "ID NFC tagu",
"nfc_linked": "NFC tag propojen",
@@ -206,6 +206,13 @@
"nfc_tags_refresh": "Opdater",
"environmental_entity_optional": "Miljøsensor (valgfrit)",
"environmental_entity_helper": "f.eks. sensor.outdoor_temperature — justerer intervallet baseret på miljøforhold",
"adaptive_prediction_enabled": "Aktivér sensordrevne forudsigelser",
"adaptive_seasonal_enabled": "Aktivér sæsonbevidsthed",
"adaptive_max_interval": "Maksimumsinterval (dage)",
"adaptive_min_interval": "Minimumsinterval (dage)",
"adaptive_ewa_alpha": "Læringsrate (alfa)",
"adaptive_enabled": "Aktivér adaptiv planlægning",
"adaptive_section_title": "Adaptiv planlægning",
"environmental_attribute_optional": "Miljøattribut (valgfrit)",
"nfc_tag_id": "NFC-tag-ID",
"nfc_linked": "NFC-tag tilknyttet",
@@ -206,6 +206,13 @@
"nfc_tags_refresh": "Aktualisieren",
"environmental_entity_optional": "Umgebungs-Sensor (optional)",
"environmental_entity_helper": "z.B. sensor.aussentemperatur — passt das Intervall an Umgebungswerte an",
"adaptive_prediction_enabled": "Sensorbasierte Vorhersagen aktivieren",
"adaptive_seasonal_enabled": "Saisonale Anpassung aktivieren",
"adaptive_max_interval": "Maximales Intervall (Tage)",
"adaptive_min_interval": "Minimales Intervall (Tage)",
"adaptive_ewa_alpha": "Lernrate (Alpha)",
"adaptive_enabled": "Adaptive Planung aktivieren",
"adaptive_section_title": "Adaptive Planung",
"environmental_attribute_optional": "Umgebungs-Attribut (optional)",
"nfc_tag_id": "NFC-Tag-ID",
"nfc_linked": "NFC-Tag verknüpft",
@@ -206,6 +206,13 @@
"nfc_tags_refresh": "Refresh",
"environmental_entity_optional": "Environmental sensor (optional)",
"environmental_entity_helper": "e.g. sensor.outdoor_temperature — adjusts the interval based on environmental conditions",
"adaptive_prediction_enabled": "Enable sensor-driven predictions",
"adaptive_seasonal_enabled": "Enable seasonal awareness",
"adaptive_max_interval": "Maximum interval (days)",
"adaptive_min_interval": "Minimum interval (days)",
"adaptive_ewa_alpha": "Learning rate (alpha)",
"adaptive_enabled": "Enable adaptive scheduling",
"adaptive_section_title": "Adaptive Scheduling",
"environmental_attribute_optional": "Environmental attribute (optional)",
"nfc_tag_id": "NFC Tag ID",
"nfc_linked": "NFC tag linked",
@@ -205,6 +205,13 @@
"nfc_tags_refresh": "Actualizar",
"environmental_entity_optional": "Sensor ambiental (opcional)",
"environmental_entity_helper": "p.ej. sensor.temperatura_exterior — ajusta el intervalo según las condiciones ambientales",
"adaptive_prediction_enabled": "Activar predicciones de sensor",
"adaptive_seasonal_enabled": "Activar conciencia estacional",
"adaptive_max_interval": "Intervalo máximo (días)",
"adaptive_min_interval": "Intervalo mínimo (días)",
"adaptive_ewa_alpha": "Tasa de aprendizaje (alpha)",
"adaptive_enabled": "Activar programación adaptativa",
"adaptive_section_title": "Programación adaptativa",
"environmental_attribute_optional": "Atributo ambiental (opcional)",
"nfc_tag_id": "ID de etiqueta NFC",
"nfc_linked": "Etiqueta NFC vinculada",
@@ -206,6 +206,13 @@
"nfc_tags_refresh": "Päivitä",
"environmental_entity_optional": "Ympäristöanturi (valinnainen)",
"environmental_entity_helper": "esim. sensor.outdoor_temperature — säätää väliä ympäristöolosuhteiden mukaan",
"adaptive_prediction_enabled": "Ota anturiperusteiset ennusteet käyttöön",
"adaptive_seasonal_enabled": "Ota kausitietoisuus käyttöön",
"adaptive_max_interval": "Enimmäisaikaväli (päivää)",
"adaptive_min_interval": "Vähimmäisaikaväli (päivää)",
"adaptive_ewa_alpha": "Oppimisnopeus (alfa)",
"adaptive_enabled": "Ota mukautuva aikataulutus käyttöön",
"adaptive_section_title": "Mukautuva aikataulutus",
"environmental_attribute_optional": "Ympäristöattribuutti (valinnainen)",
"nfc_tag_id": "NFC-tunnisteen tunnus",
"nfc_linked": "NFC-tunniste linkitetty",
@@ -205,6 +205,13 @@
"nfc_tags_refresh": "Actualiser",
"environmental_entity_optional": "Capteur d'environnement (optionnel)",
"environmental_entity_helper": "ex. sensor.temperature_exterieure — ajuste l'intervalle selon les conditions environnementales",
"adaptive_prediction_enabled": "Activer les prédictions capteur",
"adaptive_seasonal_enabled": "Activer la sensibilité saisonnière",
"adaptive_max_interval": "Intervalle maximum (jours)",
"adaptive_min_interval": "Intervalle minimum (jours)",
"adaptive_ewa_alpha": "Taux d'apprentissage (alpha)",
"adaptive_enabled": "Activer la planification adaptative",
"adaptive_section_title": "Planification adaptative",
"environmental_attribute_optional": "Attribut d'environnement (optionnel)",
"nfc_tag_id": "ID tag NFC",
"nfc_linked": "Tag NFC lié",
@@ -206,6 +206,13 @@
"nfc_tags_refresh": "रिफ़्रेश करें",
"environmental_entity_optional": "पर्यावरण सेंसर (वैकल्पिक)",
"environmental_entity_helper": "उदा. sensor.outdoor_temperature — पर्यावरणीय स्थितियों के आधार पर अंतराल समायोजित करता है",
"adaptive_prediction_enabled": "सेंसर-संचालित पूर्वानुमान सक्षम करें",
"adaptive_seasonal_enabled": "मौसमी जागरूकता सक्षम करें",
"adaptive_max_interval": "अधिकतम अंतराल (दिन)",
"adaptive_min_interval": "न्यूनतम अंतराल (दिन)",
"adaptive_ewa_alpha": "अधिगम दर (अल्फा)",
"adaptive_enabled": "अनुकूली अनुसूचन सक्षम करें",
"adaptive_section_title": "अनुकूली अनुसूचन",
"environmental_attribute_optional": "पर्यावरण विशेषता (वैकल्पिक)",
"nfc_tag_id": "NFC टैग ID",
"nfc_linked": "NFC टैग लिंक किया गया",
@@ -206,6 +206,13 @@
"nfc_tags_refresh": "Frissítés",
"environmental_entity_optional": "Környezeti érzékelő (opcionális)",
"environmental_entity_helper": "pl. sensor.outdoor_temperature — a környezeti feltételek alapján igazítja az intervallumot",
"adaptive_prediction_enabled": "Érzékelővezérelt előrejelzések engedélyezése",
"adaptive_seasonal_enabled": "Szezonális igazodás engedélyezése",
"adaptive_max_interval": "Maximális intervallum (nap)",
"adaptive_min_interval": "Minimális intervallum (nap)",
"adaptive_ewa_alpha": "Tanulási ráta (alfa)",
"adaptive_enabled": "Adaptív ütemezés engedélyezése",
"adaptive_section_title": "Adaptív ütemezés",
"environmental_attribute_optional": "Környezeti attribútum (opcionális)",
"nfc_tag_id": "NFC címke azonosító",
"nfc_linked": "NFC címke hozzárendelve",
@@ -205,6 +205,13 @@
"nfc_tags_refresh": "Aggiorna",
"environmental_entity_optional": "Sensore ambientale (opzionale)",
"environmental_entity_helper": "es. sensor.temperatura_esterna — regola l'intervallo in base alle condizioni ambientali",
"adaptive_prediction_enabled": "Abilita previsioni sensore",
"adaptive_seasonal_enabled": "Abilita consapevolezza stagionale",
"adaptive_max_interval": "Intervallo massimo (giorni)",
"adaptive_min_interval": "Intervallo minimo (giorni)",
"adaptive_ewa_alpha": "Tasso di apprendimento (alpha)",
"adaptive_enabled": "Abilita pianificazione adattiva",
"adaptive_section_title": "Pianificazione adattiva",
"environmental_attribute_optional": "Attributo ambientale (opzionale)",
"nfc_tag_id": "ID tag NFC",
"nfc_linked": "Tag NFC collegato",
@@ -206,6 +206,13 @@
"nfc_tags_refresh": "更新",
"environmental_entity_optional": "環境センサー(任意)",
"environmental_entity_helper": "例: sensor.outdoor_temperature — 環境条件に応じて間隔を調整します",
"adaptive_prediction_enabled": "センサー駆動の予測を有効にする",
"adaptive_seasonal_enabled": "季節認識を有効にする",
"adaptive_max_interval": "最大間隔 (日)",
"adaptive_min_interval": "最小間隔 (日)",
"adaptive_ewa_alpha": "学習率 (アルファ)",
"adaptive_enabled": "適応スケジューリングを有効にする",
"adaptive_section_title": "適応スケジューリング",
"environmental_attribute_optional": "環境属性(任意)",
"nfc_tag_id": "NFCタグID",
"nfc_linked": "NFCタグ連携済み",
@@ -206,6 +206,13 @@
"nfc_tags_refresh": "새로 고침",
"environmental_entity_optional": "환경 센서 (선택)",
"environmental_entity_helper": "예: sensor.outdoor_temperature — 환경 조건에 따라 주기를 조정합니다",
"adaptive_prediction_enabled": "센서 기반 예측 사용",
"adaptive_seasonal_enabled": "계절 인식 사용",
"adaptive_max_interval": "최대 주기(일)",
"adaptive_min_interval": "최소 주기(일)",
"adaptive_ewa_alpha": "학습률(alpha)",
"adaptive_enabled": "적응형 일정 사용",
"adaptive_section_title": "적응형 일정",
"environmental_attribute_optional": "환경 속성 (선택)",
"nfc_tag_id": "NFC 태그 ID",
"nfc_linked": "NFC 태그 연결됨",
@@ -206,6 +206,13 @@
"nfc_tags_refresh": "Oppdater",
"environmental_entity_optional": "Miljøsensor (valgfritt)",
"environmental_entity_helper": "f.eks. sensor.outdoor_temperature — justerer intervallet basert på miljøforhold",
"adaptive_prediction_enabled": "Aktiver sensordrevne prognoser",
"adaptive_seasonal_enabled": "Aktiver sesongbevissthet",
"adaptive_max_interval": "Største intervall (dager)",
"adaptive_min_interval": "Minste intervall (dager)",
"adaptive_ewa_alpha": "Læringsrate (alfa)",
"adaptive_enabled": "Aktiver adaptiv planlegging",
"adaptive_section_title": "Adaptiv planlegging",
"environmental_attribute_optional": "Miljøattributt (valgfritt)",
"nfc_tag_id": "NFC-brikke-ID",
"nfc_linked": "NFC-brikke koblet",
@@ -205,6 +205,13 @@
"nfc_tags_refresh": "Vernieuwen",
"environmental_entity_optional": "Omgevingssensor (optioneel)",
"environmental_entity_helper": "bv. sensor.buitentemperatuur — past het interval aan op basis van omgevingswaarden",
"adaptive_prediction_enabled": "Sensorgestuurde voorspellingen inschakelen",
"adaptive_seasonal_enabled": "Seizoensbewustzijn inschakelen",
"adaptive_max_interval": "Maximaal interval (dagen)",
"adaptive_min_interval": "Minimaal interval (dagen)",
"adaptive_ewa_alpha": "Leersnelheid (alpha)",
"adaptive_enabled": "Adaptieve planning inschakelen",
"adaptive_section_title": "Adaptieve planning",
"environmental_attribute_optional": "Omgevingsattribuut (optioneel)",
"nfc_tag_id": "NFC-tag-ID",
"nfc_linked": "NFC-tag gekoppeld",
@@ -205,6 +205,13 @@
"nfc_tags_refresh": "Odśwież",
"environmental_entity_optional": "Czujnik środowiskowy (opcjonalne)",
"environmental_entity_helper": "np. sensor.outdoor_temperature — dostosowuje interwał na podstawie warunków środowiskowych",
"adaptive_prediction_enabled": "Włącz predykcje sterowane czujnikami",
"adaptive_seasonal_enabled": "Włącz świadomość sezonową",
"adaptive_max_interval": "Maksymalny interwał (dni)",
"adaptive_min_interval": "Minimalny interwał (dni)",
"adaptive_ewa_alpha": "Tempo uczenia (alpha)",
"adaptive_enabled": "Włącz adaptacyjne planowanie",
"adaptive_section_title": "Adaptacyjne planowanie",
"environmental_attribute_optional": "Atrybut środowiskowy (opcjonalne)",
"nfc_tag_id": "ID tagu NFC",
"nfc_linked": "Tag NFC powiązany",
@@ -206,6 +206,13 @@
"nfc_tags_refresh": "Atualizar",
"environmental_entity_optional": "Sensor ambiental (opcional)",
"environmental_entity_helper": "ex.: sensor.outdoor_temperature — ajusta o intervalo com base nas condições ambientais",
"adaptive_prediction_enabled": "Ativar previsões baseadas em sensor",
"adaptive_seasonal_enabled": "Ativar sazonalidade",
"adaptive_max_interval": "Intervalo máximo (dias)",
"adaptive_min_interval": "Intervalo mínimo (dias)",
"adaptive_ewa_alpha": "Taxa de aprendizado (alpha)",
"adaptive_enabled": "Ativar agendamento adaptativo",
"adaptive_section_title": "Agendamento adaptativo",
"environmental_attribute_optional": "Atributo ambiental (opcional)",
"nfc_tag_id": "ID da tag NFC",
"nfc_linked": "Tag NFC vinculada",
@@ -205,6 +205,13 @@
"nfc_tags_refresh": "Atualizar",
"environmental_entity_optional": "Sensor ambiental (opcional)",
"environmental_entity_helper": "ex. sensor.temperatura_exterior — ajusta o intervalo segundo as condições ambientais",
"adaptive_prediction_enabled": "Ativar previsões baseadas em sensores",
"adaptive_seasonal_enabled": "Ativar consciência sazonal",
"adaptive_max_interval": "Intervalo máximo (dias)",
"adaptive_min_interval": "Intervalo mínimo (dias)",
"adaptive_ewa_alpha": "Taxa de aprendizagem (alfa)",
"adaptive_enabled": "Ativar agendamento adaptativo",
"adaptive_section_title": "Agendamento Adaptativo",
"environmental_attribute_optional": "Atributo ambiental (opcional)",
"nfc_tag_id": "ID da etiqueta NFC",
"nfc_linked": "Etiqueta NFC associada",
@@ -205,6 +205,13 @@
"nfc_tags_refresh": "Обновить",
"environmental_entity_optional": "Датчик окружающей среды (опционально)",
"environmental_entity_helper": "напр. sensor.outdoor_temperature — корректирует интервал в зависимости от условий",
"adaptive_prediction_enabled": "Включить прогнозы на основе датчиков",
"adaptive_seasonal_enabled": "Учитывать сезонность",
"adaptive_max_interval": "Максимальный интервал (дни)",
"adaptive_min_interval": "Минимальный интервал (дни)",
"adaptive_ewa_alpha": "Скорость обучения (альфа)",
"adaptive_enabled": "Включить адаптивное планирование",
"adaptive_section_title": "Адаптивное планирование",
"environmental_attribute_optional": "Атрибут среды (опционально)",
"nfc_tag_id": "ID NFC-метки",
"nfc_linked": "NFC-метка привязана",
@@ -205,6 +205,13 @@
"nfc_tags_refresh": "Uppdatera",
"environmental_entity_optional": "Miljösensor (valfritt)",
"environmental_entity_helper": "t.ex. sensor.outdoor_temperature — justerar intervallet baserat på miljöförhållanden",
"adaptive_prediction_enabled": "Aktivera sensorstyrda prediktioner",
"adaptive_seasonal_enabled": "Aktivera säsongsmedvetenhet",
"adaptive_max_interval": "Största intervall (dagar)",
"adaptive_min_interval": "Minsta intervall (dagar)",
"adaptive_ewa_alpha": "Inlärningshastighet (alpha)",
"adaptive_enabled": "Aktivera adaptiv schemaläggning",
"adaptive_section_title": "Adaptiv schemaläggning",
"environmental_attribute_optional": "Miljöattribut (valfritt)",
"nfc_tag_id": "NFC-tagg-ID",
"nfc_linked": "NFC-tagg länkad",
@@ -206,6 +206,13 @@
"nfc_tags_refresh": "Yenile",
"environmental_entity_optional": "Çevresel sensör (isteğe bağlı)",
"environmental_entity_helper": "örn. sensor.outdoor_temperature — aralığı çevresel koşullara göre ayarlar",
"adaptive_prediction_enabled": "Sensör tabanlı tahminleri etkinleştir",
"adaptive_seasonal_enabled": "Mevsimsel farkındalığı etkinleştir",
"adaptive_max_interval": "Maksimum aralık (gün)",
"adaptive_min_interval": "Minimum aralık (gün)",
"adaptive_ewa_alpha": "Öğrenme hızı (alfa)",
"adaptive_enabled": "Uyarlanabilir zamanlamayı etkinleştir",
"adaptive_section_title": "Uyarlanabilir Zamanlama",
"environmental_attribute_optional": "Çevresel öznitelik (isteğe bağlı)",
"nfc_tag_id": "NFC Etiket Kimliği",
"nfc_linked": "NFC etiketi bağlandı",
@@ -205,6 +205,13 @@
"nfc_tags_refresh": "Оновити",
"environmental_entity_optional": "Датчик навколишнього середовища (необов'язково)",
"environmental_entity_helper": "напр. sensor.outdoor_temperature — коригує інтервал відповідно до умов навколишнього середовища",
"adaptive_prediction_enabled": "Увімкнути прогнози за сенсорами",
"adaptive_seasonal_enabled": "Увімкнути сезонну корекцію",
"adaptive_max_interval": "Максимальний інтервал (дні)",
"adaptive_min_interval": "Мінімальний інтервал (дні)",
"adaptive_ewa_alpha": "Швидкість навчання (alpha)",
"adaptive_enabled": "Увімкнути адаптивне планування",
"adaptive_section_title": "Адаптивне планування",
"environmental_attribute_optional": "Атрибут середовища (необов'язково)",
"nfc_tag_id": "ID NFC-тега",
"nfc_linked": "NFC-тег прив'язано",
@@ -206,6 +206,13 @@
"nfc_tags_refresh": "刷新",
"environmental_entity_optional": "环境传感器 (可选)",
"environmental_entity_helper": "例如:sensor.outdoor_temperature — 根据环境条件自动调整间隔",
"adaptive_prediction_enabled": "启用基于传感器的预测",
"adaptive_seasonal_enabled": "启用季节性感知",
"adaptive_max_interval": "最大间隔 (天)",
"adaptive_min_interval": "最小间隔 (天)",
"adaptive_ewa_alpha": "学习率 (alpha)",
"adaptive_enabled": "启用自适应计划",
"adaptive_section_title": "自适应计划",
"environmental_attribute_optional": "环境属性 (可选)",
"nfc_tag_id": "NFC 标签 ID",
"nfc_linked": "NFC 标签已链接",
@@ -205,6 +205,13 @@
"nfc_tags_refresh": "Obnovit",
"environmental_entity_optional": "Senzor prostředí (volitelný)",
"environmental_entity_helper": "např. sensor.outdoor_temperature — upravuje interval podle podmínek prostředí",
"adaptive_prediction_enabled": "Povolit predikce řízené senzory",
"adaptive_seasonal_enabled": "Povolit sezónní povědomí",
"adaptive_max_interval": "Maximální interval (dny)",
"adaptive_min_interval": "Minimální interval (dny)",
"adaptive_ewa_alpha": "Rychlost učení (alpha)",
"adaptive_enabled": "Povolit adaptivní plánování",
"adaptive_section_title": "Adaptivní plánování",
"environmental_attribute_optional": "Atribut prostředí (volitelný)",
"nfc_tag_id": "ID NFC tagu",
"nfc_linked": "NFC tag propojen",
@@ -206,6 +206,13 @@
"nfc_tags_refresh": "Opdater",
"environmental_entity_optional": "Miljøsensor (valgfrit)",
"environmental_entity_helper": "f.eks. sensor.outdoor_temperature — justerer intervallet baseret på miljøforhold",
"adaptive_prediction_enabled": "Aktivér sensordrevne forudsigelser",
"adaptive_seasonal_enabled": "Aktivér sæsonbevidsthed",
"adaptive_max_interval": "Maksimumsinterval (dage)",
"adaptive_min_interval": "Minimumsinterval (dage)",
"adaptive_ewa_alpha": "Læringsrate (alfa)",
"adaptive_enabled": "Aktivér adaptiv planlægning",
"adaptive_section_title": "Adaptiv planlægning",
"environmental_attribute_optional": "Miljøattribut (valgfrit)",
"nfc_tag_id": "NFC-tag-ID",
"nfc_linked": "NFC-tag tilknyttet",
@@ -206,6 +206,13 @@
"nfc_tags_refresh": "Aktualisieren",
"environmental_entity_optional": "Umgebungs-Sensor (optional)",
"environmental_entity_helper": "z.B. sensor.aussentemperatur — passt das Intervall an Umgebungswerte an",
"adaptive_prediction_enabled": "Sensorbasierte Vorhersagen aktivieren",
"adaptive_seasonal_enabled": "Saisonale Anpassung aktivieren",
"adaptive_max_interval": "Maximales Intervall (Tage)",
"adaptive_min_interval": "Minimales Intervall (Tage)",
"adaptive_ewa_alpha": "Lernrate (Alpha)",
"adaptive_enabled": "Adaptive Planung aktivieren",
"adaptive_section_title": "Adaptive Planung",
"environmental_attribute_optional": "Umgebungs-Attribut (optional)",
"nfc_tag_id": "NFC-Tag-ID",
"nfc_linked": "NFC-Tag verknüpft",
@@ -206,6 +206,13 @@
"nfc_tags_refresh": "Refresh",
"environmental_entity_optional": "Environmental sensor (optional)",
"environmental_entity_helper": "e.g. sensor.outdoor_temperature — adjusts the interval based on environmental conditions",
"adaptive_prediction_enabled": "Enable sensor-driven predictions",
"adaptive_seasonal_enabled": "Enable seasonal awareness",
"adaptive_max_interval": "Maximum interval (days)",
"adaptive_min_interval": "Minimum interval (days)",
"adaptive_ewa_alpha": "Learning rate (alpha)",
"adaptive_enabled": "Enable adaptive scheduling",
"adaptive_section_title": "Adaptive Scheduling",
"environmental_attribute_optional": "Environmental attribute (optional)",
"nfc_tag_id": "NFC Tag ID",
"nfc_linked": "NFC tag linked",
@@ -205,6 +205,13 @@
"nfc_tags_refresh": "Actualizar",
"environmental_entity_optional": "Sensor ambiental (opcional)",
"environmental_entity_helper": "p.ej. sensor.temperatura_exterior — ajusta el intervalo según las condiciones ambientales",
"adaptive_prediction_enabled": "Activar predicciones de sensor",
"adaptive_seasonal_enabled": "Activar conciencia estacional",
"adaptive_max_interval": "Intervalo máximo (días)",
"adaptive_min_interval": "Intervalo mínimo (días)",
"adaptive_ewa_alpha": "Tasa de aprendizaje (alpha)",
"adaptive_enabled": "Activar programación adaptativa",
"adaptive_section_title": "Programación adaptativa",
"environmental_attribute_optional": "Atributo ambiental (opcional)",
"nfc_tag_id": "ID de etiqueta NFC",
"nfc_linked": "Etiqueta NFC vinculada",
@@ -206,6 +206,13 @@
"nfc_tags_refresh": "Päivitä",
"environmental_entity_optional": "Ympäristöanturi (valinnainen)",
"environmental_entity_helper": "esim. sensor.outdoor_temperature — säätää väliä ympäristöolosuhteiden mukaan",
"adaptive_prediction_enabled": "Ota anturiperusteiset ennusteet käyttöön",
"adaptive_seasonal_enabled": "Ota kausitietoisuus käyttöön",
"adaptive_max_interval": "Enimmäisaikaväli (päivää)",
"adaptive_min_interval": "Vähimmäisaikaväli (päivää)",
"adaptive_ewa_alpha": "Oppimisnopeus (alfa)",
"adaptive_enabled": "Ota mukautuva aikataulutus käyttöön",
"adaptive_section_title": "Mukautuva aikataulutus",
"environmental_attribute_optional": "Ympäristöattribuutti (valinnainen)",
"nfc_tag_id": "NFC-tunnisteen tunnus",
"nfc_linked": "NFC-tunniste linkitetty",
@@ -205,6 +205,13 @@
"nfc_tags_refresh": "Actualiser",
"environmental_entity_optional": "Capteur d'environnement (optionnel)",
"environmental_entity_helper": "ex. sensor.temperature_exterieure — ajuste l'intervalle selon les conditions environnementales",
"adaptive_prediction_enabled": "Activer les prédictions capteur",
"adaptive_seasonal_enabled": "Activer la sensibilité saisonnière",
"adaptive_max_interval": "Intervalle maximum (jours)",
"adaptive_min_interval": "Intervalle minimum (jours)",
"adaptive_ewa_alpha": "Taux d'apprentissage (alpha)",
"adaptive_enabled": "Activer la planification adaptative",
"adaptive_section_title": "Planification adaptative",
"environmental_attribute_optional": "Attribut d'environnement (optionnel)",
"nfc_tag_id": "ID tag NFC",
"nfc_linked": "Tag NFC lié",
@@ -206,6 +206,13 @@
"nfc_tags_refresh": "रिफ़्रेश करें",
"environmental_entity_optional": "पर्यावरण सेंसर (वैकल्पिक)",
"environmental_entity_helper": "उदा. sensor.outdoor_temperature — पर्यावरणीय स्थितियों के आधार पर अंतराल समायोजित करता है",
"adaptive_prediction_enabled": "सेंसर-संचालित पूर्वानुमान सक्षम करें",
"adaptive_seasonal_enabled": "मौसमी जागरूकता सक्षम करें",
"adaptive_max_interval": "अधिकतम अंतराल (दिन)",
"adaptive_min_interval": "न्यूनतम अंतराल (दिन)",
"adaptive_ewa_alpha": "अधिगम दर (अल्फा)",
"adaptive_enabled": "अनुकूली अनुसूचन सक्षम करें",
"adaptive_section_title": "अनुकूली अनुसूचन",
"environmental_attribute_optional": "पर्यावरण विशेषता (वैकल्पिक)",
"nfc_tag_id": "NFC टैग ID",
"nfc_linked": "NFC टैग लिंक किया गया",
@@ -206,6 +206,13 @@
"nfc_tags_refresh": "Frissítés",
"environmental_entity_optional": "Környezeti érzékelő (opcionális)",
"environmental_entity_helper": "pl. sensor.outdoor_temperature — a környezeti feltételek alapján igazítja az intervallumot",
"adaptive_prediction_enabled": "Érzékelővezérelt előrejelzések engedélyezése",
"adaptive_seasonal_enabled": "Szezonális igazodás engedélyezése",
"adaptive_max_interval": "Maximális intervallum (nap)",
"adaptive_min_interval": "Minimális intervallum (nap)",
"adaptive_ewa_alpha": "Tanulási ráta (alfa)",
"adaptive_enabled": "Adaptív ütemezés engedélyezése",
"adaptive_section_title": "Adaptív ütemezés",
"environmental_attribute_optional": "Környezeti attribútum (opcionális)",
"nfc_tag_id": "NFC címke azonosító",
"nfc_linked": "NFC címke hozzárendelve",
@@ -205,6 +205,13 @@
"nfc_tags_refresh": "Aggiorna",
"environmental_entity_optional": "Sensore ambientale (opzionale)",
"environmental_entity_helper": "es. sensor.temperatura_esterna — regola l'intervallo in base alle condizioni ambientali",
"adaptive_prediction_enabled": "Abilita previsioni sensore",
"adaptive_seasonal_enabled": "Abilita consapevolezza stagionale",
"adaptive_max_interval": "Intervallo massimo (giorni)",
"adaptive_min_interval": "Intervallo minimo (giorni)",
"adaptive_ewa_alpha": "Tasso di apprendimento (alpha)",
"adaptive_enabled": "Abilita pianificazione adattiva",
"adaptive_section_title": "Pianificazione adattiva",
"environmental_attribute_optional": "Attributo ambientale (opzionale)",
"nfc_tag_id": "ID tag NFC",
"nfc_linked": "Tag NFC collegato",
@@ -206,6 +206,13 @@
"nfc_tags_refresh": "更新",
"environmental_entity_optional": "環境センサー(任意)",
"environmental_entity_helper": "例: sensor.outdoor_temperature — 環境条件に応じて間隔を調整します",
"adaptive_prediction_enabled": "センサー駆動の予測を有効にする",
"adaptive_seasonal_enabled": "季節認識を有効にする",
"adaptive_max_interval": "最大間隔 (日)",
"adaptive_min_interval": "最小間隔 (日)",
"adaptive_ewa_alpha": "学習率 (アルファ)",
"adaptive_enabled": "適応スケジューリングを有効にする",
"adaptive_section_title": "適応スケジューリング",
"environmental_attribute_optional": "環境属性(任意)",
"nfc_tag_id": "NFCタグID",
"nfc_linked": "NFCタグ連携済み",
@@ -206,6 +206,13 @@
"nfc_tags_refresh": "새로 고침",
"environmental_entity_optional": "환경 센서 (선택)",
"environmental_entity_helper": "예: sensor.outdoor_temperature — 환경 조건에 따라 주기를 조정합니다",
"adaptive_prediction_enabled": "센서 기반 예측 사용",
"adaptive_seasonal_enabled": "계절 인식 사용",
"adaptive_max_interval": "최대 주기(일)",
"adaptive_min_interval": "최소 주기(일)",
"adaptive_ewa_alpha": "학습률(alpha)",
"adaptive_enabled": "적응형 일정 사용",
"adaptive_section_title": "적응형 일정",
"environmental_attribute_optional": "환경 속성 (선택)",
"nfc_tag_id": "NFC 태그 ID",
"nfc_linked": "NFC 태그 연결됨",
@@ -206,6 +206,13 @@
"nfc_tags_refresh": "Oppdater",
"environmental_entity_optional": "Miljøsensor (valgfritt)",
"environmental_entity_helper": "f.eks. sensor.outdoor_temperature — justerer intervallet basert på miljøforhold",
"adaptive_prediction_enabled": "Aktiver sensordrevne prognoser",
"adaptive_seasonal_enabled": "Aktiver sesongbevissthet",
"adaptive_max_interval": "Største intervall (dager)",
"adaptive_min_interval": "Minste intervall (dager)",
"adaptive_ewa_alpha": "Læringsrate (alfa)",
"adaptive_enabled": "Aktiver adaptiv planlegging",
"adaptive_section_title": "Adaptiv planlegging",
"environmental_attribute_optional": "Miljøattributt (valgfritt)",
"nfc_tag_id": "NFC-brikke-ID",
"nfc_linked": "NFC-brikke koblet",
@@ -205,6 +205,13 @@
"nfc_tags_refresh": "Vernieuwen",
"environmental_entity_optional": "Omgevingssensor (optioneel)",
"environmental_entity_helper": "bv. sensor.buitentemperatuur — past het interval aan op basis van omgevingswaarden",
"adaptive_prediction_enabled": "Sensorgestuurde voorspellingen inschakelen",
"adaptive_seasonal_enabled": "Seizoensbewustzijn inschakelen",
"adaptive_max_interval": "Maximaal interval (dagen)",
"adaptive_min_interval": "Minimaal interval (dagen)",
"adaptive_ewa_alpha": "Leersnelheid (alpha)",
"adaptive_enabled": "Adaptieve planning inschakelen",
"adaptive_section_title": "Adaptieve planning",
"environmental_attribute_optional": "Omgevingsattribuut (optioneel)",
"nfc_tag_id": "NFC-tag-ID",
"nfc_linked": "NFC-tag gekoppeld",
@@ -205,6 +205,13 @@
"nfc_tags_refresh": "Odśwież",
"environmental_entity_optional": "Czujnik środowiskowy (opcjonalne)",
"environmental_entity_helper": "np. sensor.outdoor_temperature — dostosowuje interwał na podstawie warunków środowiskowych",
"adaptive_prediction_enabled": "Włącz predykcje sterowane czujnikami",
"adaptive_seasonal_enabled": "Włącz świadomość sezonową",
"adaptive_max_interval": "Maksymalny interwał (dni)",
"adaptive_min_interval": "Minimalny interwał (dni)",
"adaptive_ewa_alpha": "Tempo uczenia (alpha)",
"adaptive_enabled": "Włącz adaptacyjne planowanie",
"adaptive_section_title": "Adaptacyjne planowanie",
"environmental_attribute_optional": "Atrybut środowiskowy (opcjonalne)",
"nfc_tag_id": "ID tagu NFC",
"nfc_linked": "Tag NFC powiązany",
@@ -206,6 +206,13 @@
"nfc_tags_refresh": "Atualizar",
"environmental_entity_optional": "Sensor ambiental (opcional)",
"environmental_entity_helper": "ex.: sensor.outdoor_temperature — ajusta o intervalo com base nas condições ambientais",
"adaptive_prediction_enabled": "Ativar previsões baseadas em sensor",
"adaptive_seasonal_enabled": "Ativar sazonalidade",
"adaptive_max_interval": "Intervalo máximo (dias)",
"adaptive_min_interval": "Intervalo mínimo (dias)",
"adaptive_ewa_alpha": "Taxa de aprendizado (alpha)",
"adaptive_enabled": "Ativar agendamento adaptativo",
"adaptive_section_title": "Agendamento adaptativo",
"environmental_attribute_optional": "Atributo ambiental (opcional)",
"nfc_tag_id": "ID da tag NFC",
"nfc_linked": "Tag NFC vinculada",
@@ -205,6 +205,13 @@
"nfc_tags_refresh": "Atualizar",
"environmental_entity_optional": "Sensor ambiental (opcional)",
"environmental_entity_helper": "ex. sensor.temperatura_exterior — ajusta o intervalo segundo as condições ambientais",
"adaptive_prediction_enabled": "Ativar previsões baseadas em sensores",
"adaptive_seasonal_enabled": "Ativar consciência sazonal",
"adaptive_max_interval": "Intervalo máximo (dias)",
"adaptive_min_interval": "Intervalo mínimo (dias)",
"adaptive_ewa_alpha": "Taxa de aprendizagem (alfa)",
"adaptive_enabled": "Ativar agendamento adaptativo",
"adaptive_section_title": "Agendamento Adaptativo",
"environmental_attribute_optional": "Atributo ambiental (opcional)",
"nfc_tag_id": "ID da etiqueta NFC",
"nfc_linked": "Etiqueta NFC associada",
@@ -205,6 +205,13 @@
"nfc_tags_refresh": "Обновить",
"environmental_entity_optional": "Датчик окружающей среды (опционально)",
"environmental_entity_helper": "напр. sensor.outdoor_temperature — корректирует интервал в зависимости от условий",
"adaptive_prediction_enabled": "Включить прогнозы на основе датчиков",
"adaptive_seasonal_enabled": "Учитывать сезонность",
"adaptive_max_interval": "Максимальный интервал (дни)",
"adaptive_min_interval": "Минимальный интервал (дни)",
"adaptive_ewa_alpha": "Скорость обучения (альфа)",
"adaptive_enabled": "Включить адаптивное планирование",
"adaptive_section_title": "Адаптивное планирование",
"environmental_attribute_optional": "Атрибут среды (опционально)",
"nfc_tag_id": "ID NFC-метки",
"nfc_linked": "NFC-метка привязана",
@@ -205,6 +205,13 @@
"nfc_tags_refresh": "Uppdatera",
"environmental_entity_optional": "Miljösensor (valfritt)",
"environmental_entity_helper": "t.ex. sensor.outdoor_temperature — justerar intervallet baserat på miljöförhållanden",
"adaptive_prediction_enabled": "Aktivera sensorstyrda prediktioner",
"adaptive_seasonal_enabled": "Aktivera säsongsmedvetenhet",
"adaptive_max_interval": "Största intervall (dagar)",
"adaptive_min_interval": "Minsta intervall (dagar)",
"adaptive_ewa_alpha": "Inlärningshastighet (alpha)",
"adaptive_enabled": "Aktivera adaptiv schemaläggning",
"adaptive_section_title": "Adaptiv schemaläggning",
"environmental_attribute_optional": "Miljöattribut (valfritt)",
"nfc_tag_id": "NFC-tagg-ID",
"nfc_linked": "NFC-tagg länkad",
@@ -206,6 +206,13 @@
"nfc_tags_refresh": "Yenile",
"environmental_entity_optional": "Çevresel sensör (isteğe bağlı)",
"environmental_entity_helper": "örn. sensor.outdoor_temperature — aralığı çevresel koşullara göre ayarlar",
"adaptive_prediction_enabled": "Sensör tabanlı tahminleri etkinleştir",
"adaptive_seasonal_enabled": "Mevsimsel farkındalığı etkinleştir",
"adaptive_max_interval": "Maksimum aralık (gün)",
"adaptive_min_interval": "Minimum aralık (gün)",
"adaptive_ewa_alpha": "Öğrenme hızı (alfa)",
"adaptive_enabled": "Uyarlanabilir zamanlamayı etkinleştir",
"adaptive_section_title": "Uyarlanabilir Zamanlama",
"environmental_attribute_optional": "Çevresel öznitelik (isteğe bağlı)",
"nfc_tag_id": "NFC Etiket Kimliği",
"nfc_linked": "NFC etiketi bağlandı",
@@ -205,6 +205,13 @@
"nfc_tags_refresh": "Оновити",
"environmental_entity_optional": "Датчик навколишнього середовища (необов'язково)",
"environmental_entity_helper": "напр. sensor.outdoor_temperature — коригує інтервал відповідно до умов навколишнього середовища",
"adaptive_prediction_enabled": "Увімкнути прогнози за сенсорами",
"adaptive_seasonal_enabled": "Увімкнути сезонну корекцію",
"adaptive_max_interval": "Максимальний інтервал (дні)",
"adaptive_min_interval": "Мінімальний інтервал (дні)",
"adaptive_ewa_alpha": "Швидкість навчання (alpha)",
"adaptive_enabled": "Увімкнути адаптивне планування",
"adaptive_section_title": "Адаптивне планування",
"environmental_attribute_optional": "Атрибут середовища (необов'язково)",
"nfc_tag_id": "ID NFC-тега",
"nfc_linked": "NFC-тег прив'язано",
@@ -206,6 +206,13 @@
"nfc_tags_refresh": "刷新",
"environmental_entity_optional": "环境传感器 (可选)",
"environmental_entity_helper": "例如:sensor.outdoor_temperature — 根据环境条件自动调整间隔",
"adaptive_prediction_enabled": "启用基于传感器的预测",
"adaptive_seasonal_enabled": "启用季节性感知",
"adaptive_max_interval": "最大间隔 (天)",
"adaptive_min_interval": "最小间隔 (天)",
"adaptive_ewa_alpha": "学习率 (alpha)",
"adaptive_enabled": "启用自适应计划",
"adaptive_section_title": "自适应计划",
"environmental_attribute_optional": "环境属性 (可选)",
"nfc_tag_id": "NFC 标签 ID",
"nfc_linked": "NFC 标签已链接",
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,2 +1,2 @@
/*! maintenance_supporter frontend 2.55.0 */
var S="2.55.0";var l="maintenance-supporter",T=`ll-strategy-dashboard-${l}`,D="hui-maintenance-supporter-strategy-editor",C=`/maintenance_supporter_strategy/maintenance-dashboard-strategy.js?v=${S}`,m=null;function v(){return m||(m=import(C)),m}async function I(){let r=await v();if(!r.MaintenanceDashboardStrategy)throw new Error("[maintenance-supporter] strategy bundle loaded but did not export MaintenanceDashboardStrategy");return r.MaintenanceDashboardStrategy}var p=class extends HTMLElement{static getCreateSuggestions(c){return{title:"Maintenance Supporter",icon:"mdi:wrench-clock"}}static async getConfigElement(){return await v(),document.createElement(D)}static async generate(c,f){return(await I()).generate(c,f)}};function M(){try{customElements.define(T,p)}catch{}}M();var w=window;w.customStrategies=w.customStrategies||[];w.customStrategies.some(r=>r.type===l&&r.strategyType==="dashboard")||w.customStrategies.push({type:l,strategyType:"dashboard",name:"Maintenance Supporter",description:"Auto-generated dashboard. Group views by area, status, floor, or due date \u2014 picked from the strategy editor or YAML.",documentationURL:"https://github.com/iluebbe/maintenance_supporter#dashboard-strategy"});(()=>{let r=window;if(r.__msStrategyHealActive)return;r.__msStrategyHealActive=!0;let c=/^\/(auth|config|developer-tools|profile|hassio|history|logbook|map|media-browser|energy|todo|calendar)\b/,f=/Timeout waiting for strategy element ll-strategy-(dashboard-)?maintenance-supporter/i,g=`custom:${l}`;function R(a){let t=[document.documentElement],n=0;for(;t.length&&n<9e3;){let o=t.pop();if(n++,!o)continue;let e=o;if(e.nodeType===1&&e.tagName&&e.tagName.toLowerCase()===a)return e;e.shadowRoot&&t.push(e.shadowRoot);let i=o.children;if(i)for(let d of Array.from(i))t.push(d)}return null}function k(a){let t=a?.views;if(!Array.isArray(t)||!t.length)return null;let n=window.location.pathname.split("/").filter(Boolean).pop()||"",o=t.find(i=>i?.path===n);if(o)return o;let e=Number(n);return Number.isInteger(e)&&t[e]?t[e]:t[0]}function b(){try{let t=R("ha-panel-lovelace")?.lovelace;if(!t)return!1;let n=o=>o?.type;for(let o of[t.config,t.rawConfig]){if(!o)continue;if(n(o.strategy)===g)return!0;let e=k(o);if(e&&n(e.strategy)===g)return!0}return!1}catch{return!1}}function A(){let a=!1,t=0,n=!1,o=!1,e=[document.documentElement],i=0;for(;e.length&&i<9e3;){let d=e.pop();if(i++,!d)continue;let u=d;if(u.nodeType===1&&u.tagName){let s=u.tagName.toLowerCase();(s==="hui-view"||s==="hui-sections-view")&&(a=!0),(s==="ha-card"||s==="hui-card")&&t++,s==="hui-empty-state-card"&&(o=!0),s==="hui-error-card"&&f.test(u.textContent||"")&&(n=!0)}u.shadowRoot&&e.push(u.shadowRoot);let E=d.children;if(E)for(let s of Array.from(E))e.push(s)}return n?!0:o?!1:a&&t<3&&b()}let N="/maintenance_supporter_strategy_shim.js",y=0,_=0;function L(){let a=Date.now();a-_<5e3||y>=3||(_=a,y+=1,import(`${N}?heal=${a}`).catch(()=>{}).finally(()=>{let t=window.location.pathname+window.location.search;history.pushState(null,"","/lovelace"),window.dispatchEvent(new CustomEvent("location-changed")),window.setTimeout(()=>{history.pushState(null,"",t),window.dispatchEvent(new CustomEvent("location-changed"))},200)}))}function h(){if(c.test(window.location.pathname))return;let a=0,t=Date.now(),n=window.setInterval(()=>{a++;try{if(Date.now()-t<6e3)return;if(c.test(window.location.pathname)){window.clearInterval(n);return}A()?L():window.clearInterval(n),a>=30&&window.clearInterval(n)}catch{window.clearInterval(n)}},500)}try{document.readyState==="loading"?window.addEventListener("DOMContentLoaded",h):h(),window.addEventListener("location-changed",()=>{c.test(window.location.pathname)||h()})}catch{}})();
/*! maintenance_supporter frontend 2.56.0 */
var S="2.56.0";var l="maintenance-supporter",T=`ll-strategy-dashboard-${l}`,D="hui-maintenance-supporter-strategy-editor",C=`/maintenance_supporter_strategy/maintenance-dashboard-strategy.js?v=${S}`,m=null;function v(){return m||(m=import(C)),m}async function I(){let r=await v();if(!r.MaintenanceDashboardStrategy)throw new Error("[maintenance-supporter] strategy bundle loaded but did not export MaintenanceDashboardStrategy");return r.MaintenanceDashboardStrategy}var p=class extends HTMLElement{static getCreateSuggestions(c){return{title:"Maintenance Supporter",icon:"mdi:wrench-clock"}}static async getConfigElement(){return await v(),document.createElement(D)}static async generate(c,f){return(await I()).generate(c,f)}};function M(){try{customElements.define(T,p)}catch{}}M();var w=window;w.customStrategies=w.customStrategies||[];w.customStrategies.some(r=>r.type===l&&r.strategyType==="dashboard")||w.customStrategies.push({type:l,strategyType:"dashboard",name:"Maintenance Supporter",description:"Auto-generated dashboard. Group views by area, status, floor, or due date \u2014 picked from the strategy editor or YAML.",documentationURL:"https://github.com/iluebbe/maintenance_supporter#dashboard-strategy"});(()=>{let r=window;if(r.__msStrategyHealActive)return;r.__msStrategyHealActive=!0;let c=/^\/(auth|config|developer-tools|profile|hassio|history|logbook|map|media-browser|energy|todo|calendar)\b/,f=/Timeout waiting for strategy element ll-strategy-(dashboard-)?maintenance-supporter/i,g=`custom:${l}`;function R(a){let t=[document.documentElement],n=0;for(;t.length&&n<9e3;){let o=t.pop();if(n++,!o)continue;let e=o;if(e.nodeType===1&&e.tagName&&e.tagName.toLowerCase()===a)return e;e.shadowRoot&&t.push(e.shadowRoot);let i=o.children;if(i)for(let d of Array.from(i))t.push(d)}return null}function k(a){let t=a?.views;if(!Array.isArray(t)||!t.length)return null;let n=window.location.pathname.split("/").filter(Boolean).pop()||"",o=t.find(i=>i?.path===n);if(o)return o;let e=Number(n);return Number.isInteger(e)&&t[e]?t[e]:t[0]}function b(){try{let t=R("ha-panel-lovelace")?.lovelace;if(!t)return!1;let n=o=>o?.type;for(let o of[t.config,t.rawConfig]){if(!o)continue;if(n(o.strategy)===g)return!0;let e=k(o);if(e&&n(e.strategy)===g)return!0}return!1}catch{return!1}}function A(){let a=!1,t=0,n=!1,o=!1,e=[document.documentElement],i=0;for(;e.length&&i<9e3;){let d=e.pop();if(i++,!d)continue;let u=d;if(u.nodeType===1&&u.tagName){let s=u.tagName.toLowerCase();(s==="hui-view"||s==="hui-sections-view")&&(a=!0),(s==="ha-card"||s==="hui-card")&&t++,s==="hui-empty-state-card"&&(o=!0),s==="hui-error-card"&&f.test(u.textContent||"")&&(n=!0)}u.shadowRoot&&e.push(u.shadowRoot);let E=d.children;if(E)for(let s of Array.from(E))e.push(s)}return n?!0:o?!1:a&&t<3&&b()}let N="/maintenance_supporter_strategy_shim.js",y=0,_=0;function L(){let a=Date.now();a-_<5e3||y>=3||(_=a,y+=1,import(`${N}?heal=${a}`).catch(()=>{}).finally(()=>{let t=window.location.pathname+window.location.search;history.pushState(null,"","/lovelace"),window.dispatchEvent(new CustomEvent("location-changed")),window.setTimeout(()=>{history.pushState(null,"",t),window.dispatchEvent(new CustomEvent("location-changed"))},200)}))}function h(){if(c.test(window.location.pathname))return;let a=0,t=Date.now(),n=window.setInterval(()=>{a++;try{if(Date.now()-t<6e3)return;if(c.test(window.location.pathname)){window.clearInterval(n);return}A()?L():window.clearInterval(n),a>=30&&window.clearInterval(n)}catch{window.clearInterval(n)}},500)}try{document.readyState==="loading"?window.addEventListener("DOMContentLoaded",h):h(),window.addEventListener("location-changed",()=>{c.test(window.location.pathname)||h()})}catch{}})();
@@ -0,0 +1,222 @@
/*! maintenance_supporter frontend 2.56.0 */
import{a as d}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-LJXSDCLS.js";import{a as u}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-I7J3AORE.js";import{a,b as _,c as t,f as l,g as h,i as g,j as n,n as i,p as v}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-C5W5B43R.js";var r=class extends h{constructor(){super(...arguments);this._open=!1;this._loading=!1;this._adopting=!1;this._error="";this._sensors=[];this._selected=new Set;this._users=[];this._responsible="";this._localeReady=!1;this._userService=null;this._toggle=s=>{let o=new Set(this._selected);o.has(s)?o.delete(s):o.add(s),this._selected=o};this._toggleAll=()=>{this._selected.size===this._sensors.length?this._selected=new Set:this._selected=new Set(this._sensors.map(s=>s.entity_id))};this._adopt=async()=>{if(!(this._selected.size===0||this._adopting)){this._adopting=!0,this._error="";try{let s=this._sensors.filter(e=>this._selected.has(e.entity_id)).map(e=>({entity_id:e.entity_id,name:e.name,entry_id:e.suggested_entry_id??void 0,object_name:e.suggested_object_name,device_id:e.device_id??void 0,part_id:e.suggested_part_id??void 0,responsible_user_id:this._responsible||void 0})),o=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/problem_sensors/adopt",selections:s});this.dispatchEvent(new CustomEvent("problem-sensors-adopted",{bubbles:!0,composed:!0,detail:o})),this._open=!1}catch(s){this._error=d(s,this._lang)}finally{this._adopting=!1}}}}get _lang(){return this.hass?.language||"en"}updated(s){s.has("hass")&&this.hass&&!this._localeReady&&(this._localeReady=!0,v(this._lang).then(()=>this.requestUpdate()))}async open(){this._open=!0,this._loading=!0,this._error="",this._sensors=[],this._selected=new Set,this._responsible="";try{this._userService?this._userService.updateHass(this.hass):this._userService=new u(this.hass);let[s,o]=await Promise.all([this.hass.connection.sendMessagePromise({type:"maintenance_supporter/problem_sensors/discover"}),this._userService.getUsers().catch(()=>[])]);this._sensors=s.sensors||[],this._selected=new Set(this._sensors.map(e=>e.entity_id)),this._users=o}catch(s){this._error=d(s,this._lang)}finally{this._loading=!1}}_close(){this._open=!1}render(){if(!this._open)return t``;let s=this._lang,o=this._sensors.length>0&&this._selected.size===this._sensors.length;return t`
<div class="overlay" @click=${this._close}>
<div class="card" @click=${e=>e.stopPropagation()}>
<div class="title">${i("adopt_problem_title",s)}</div>
<div class="hint">${i("adopt_problem_hint",s)}</div>
${this._error?t`<div class="error">${this._error}</div>`:l}
${this._loading?t`<div class="loading">…</div>`:this._sensors.length===0?t`<div class="empty">${i("adopt_problem_none",s)}</div>`:t`
<label class="select-all">
<input
type="checkbox"
.checked=${o}
@change=${this._toggleAll}
/>
<span>${i("selected",s)}: ${this._selected.size} / ${this._sensors.length}</span>
</label>
<div class="list">
${this._sensors.map(e=>{let m=this._selected.has(e.entity_id),p=e.state==="on",c=[e.device_name,e.area_name].filter(Boolean).join(" \xB7 ");return t`
<label class="row">
<input
type="checkbox"
.checked=${m}
@change=${()=>this._toggle(e.entity_id)}
/>
<div class="row-main">
<div class="row-top">
<span class="row-name">${e.name}</span>
<span class="chip ${p?"chip-active":"chip-ok"}">
${p?i("adopt_problem_active",s):i("adopt_problem_ok",s)}
</span>
</div>
${c?t`<div class="row-sub">${c}</div>`:l}
<div class="row-target">
${e.suggested_object_name}${e.suggested_entry_id?l:t` <span class="new-tag">${i("adopt_problem_new_object",s)}</span>`}
</div>
${e.suggested_part_name?t`<div class="row-part">
<ha-icon icon="mdi:package-variant-closed"></ha-icon>
${i("adopt_problem_part",s).replace("{name}",e.suggested_part_name)}
</div>`:l}
</div>
</label>
`})}
</div>
`}
${!this._loading&&this._sensors.length>0&&this._users.length>0?t`
<label class="responsible">
<span>${i("adopt_problem_responsible",s)}</span>
<select
.value=${this._responsible}
@change=${e=>{this._responsible=e.target.value}}
>
<option value="" ?selected=${!this._responsible}>${i("no_user_assigned",s)}</option>
${this._users.map(e=>t`<option value=${e.id} ?selected=${e.id===this._responsible}>${e.name}</option>`)}
</select>
</label>
`:l}
<div class="actions">
<ha-button appearance="plain" @click=${this._close}>
${i("cancel",s)}
</ha-button>
<ha-button
@click=${this._adopt}
.disabled=${this._selected.size===0||this._adopting}
>
${i("adopt_problem_adopt",s)}
</ha-button>
</div>
</div>
</div>
`}};r.styles=_`
.overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.card {
background: var(--card-background-color, #fff);
color: var(--primary-text-color);
border-radius: 12px;
padding: 20px;
display: flex;
flex-direction: column;
gap: 12px;
min-width: min(360px, calc(100vw - 24px));
max-width: 560px;
width: 90vw;
max-height: 80vh;
overflow: hidden;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
}
.title {
font-size: 18px;
font-weight: 500;
}
.hint {
color: var(--secondary-text-color);
font-size: 13px;
}
.error {
color: var(--error-color, #f44336);
font-size: 13px;
}
.loading,
.empty {
color: var(--secondary-text-color);
font-size: 14px;
padding: 12px 0;
}
.select-all {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
color: var(--secondary-text-color);
cursor: pointer;
}
.select-all input {
cursor: pointer;
}
.list {
display: flex;
flex-direction: column;
gap: 6px;
overflow-y: auto;
max-height: 50vh;
}
.row {
display: flex;
align-items: flex-start;
gap: 10px;
padding: 8px;
border: 1px solid var(--divider-color);
border-radius: 6px;
cursor: pointer;
}
.row input {
margin-top: 2px;
cursor: pointer;
}
.row-main {
display: flex;
flex-direction: column;
gap: 2px;
min-width: 0;
flex: 1;
}
.row-top {
display: flex;
align-items: center;
gap: 8px;
}
.row-name {
font-weight: 500;
font-size: 13px;
}
.row-sub {
color: var(--secondary-text-color);
font-size: 12px;
}
.row-target {
color: var(--secondary-text-color);
font-size: 12px;
}
.row-part {
color: var(--secondary-text-color);
font-size: 12px;
display: flex;
align-items: center;
gap: 4px;
}
.row-part ha-icon {
--mdc-icon-size: 14px;
}
.new-tag {
font-style: italic;
}
.chip {
font-size: 11px;
padding: 1px 8px;
border-radius: 10px;
white-space: nowrap;
}
.chip-active {
background: var(--error-color, #f44336);
color: #fff;
}
.chip-ok {
background: var(--divider-color);
color: var(--secondary-text-color);
}
.responsible {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
color: var(--secondary-text-color);
flex-wrap: wrap;
}
.responsible select {
flex: 1;
min-width: 140px;
padding: 4px 6px;
border-radius: 4px;
border: 1px solid var(--divider-color);
background: var(--card-background-color, #fff);
color: var(--primary-text-color);
font-size: 13px;
}
.actions {
display: flex;
justify-content: flex-end;
gap: 8px;
padding-top: 8px;
}
`,a([g({attribute:!1})],r.prototype,"hass",2),a([n()],r.prototype,"_open",2),a([n()],r.prototype,"_loading",2),a([n()],r.prototype,"_adopting",2),a([n()],r.prototype,"_error",2),a([n()],r.prototype,"_sensors",2),a([n()],r.prototype,"_selected",2),a([n()],r.prototype,"_users",2),a([n()],r.prototype,"_responsible",2);customElements.get("maintenance-adopt-problem-sensors-dialog")||customElements.define("maintenance-adopt-problem-sensors-dialog",r);export{r as MaintenanceAdoptProblemSensorsDialog};
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.56.0 */
var i=[{key:"name",labelKey:"name",required:!0},{key:"manufacturer",labelKey:"manufacturer"},{key:"model",labelKey:"model"},{key:"serial_number",labelKey:"serial_number_label"},{key:"installation_date",labelKey:"installed"},{key:"warranty_expiry",labelKey:"warranty"},{key:"area_id",labelKey:"area"},{key:"documentation_url",labelKey:"documentation_url_label"},{key:"notes",labelKey:"object_notes_label"},{key:"task_count",labelKey:"tasks"},{key:"actions",labelKey:"actions"}],s=i.map(n=>n.key),l=["name","manufacturer","model","serial_number","installation_date","warranty_expiry","area_id","task_count","actions"];function c(n){if(!Array.isArray(n))return[...l];let o=new Set,e=[];for(let a of n)typeof a=="string"&&s.includes(a)&&!o.has(a)&&(o.add(a),e.push(a));return e.length?(e.includes("name")||e.unshift("name"),e):[...l]}function u(n,o,e){let a=new Blob([n],{type:e}),r=URL.createObjectURL(a),t=document.createElement("a");t.href=r,t.download=o,t.target="_blank",t.rel="noopener",t.style.display="none",document.body.appendChild(t),t.dispatchEvent(new MouseEvent("click")),document.body.removeChild(t),setTimeout(()=>URL.revokeObjectURL(r),6e4)}function y(n,o){let e=document.createElement("a");e.href=n,e.download=o,e.target="_blank",e.rel="noopener",e.style.display="none",document.body.appendChild(e),e.dispatchEvent(new MouseEvent("click")),document.body.removeChild(e)}export{i as a,l as b,c,u as d,y as e};
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.56.0 */
var r=class{constructor(s){this.usersCache=null;this.cacheTimestamp=0;this.CACHE_TTL_MS=6e4;this.hass=s}updateHass(s){this.hass=s}async getUsers(s=!1){let e=Date.now();if(!s&&this.usersCache&&e-this.cacheTimestamp<this.CACHE_TTL_MS)return this.usersCache;try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/users/list"});return this.usersCache=t.users,this.cacheTimestamp=e,this.usersCache}catch(t){return console.error("Failed to fetch users:",t),this.usersCache||[]}}async assignUser(s,e,t){await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/task/assign_user",entry_id:s,task_id:e,user_id:t})}async getTasksByUser(s){return(await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/tasks/by_user",user_id:s})).tasks}getUserName(s){return!s||!this.usersCache?null:this.usersCache.find(t=>t.id===s)?.name||null}getUser(s){return!s||!this.usersCache?null:this.usersCache.find(e=>e.id===s)||null}getCurrentUserId(){return this.hass.user?.id||null}isCurrentUser(s){return s?s===this.getCurrentUserId():!1}clearCache(){this.usersCache=null,this.cacheTimestamp=0}};export{r as a};
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.56.0 */
import{n as a}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-C5W5B43R.js";var s={name:"name",task_type:"maintenance_type",schedule_type:"schedule_type",interval_days:"interval_days",interval_anchor:"interval_anchor",warning_days:"warning_days",last_performed:"last_performed_optional",notes:"notes_optional",documentation_url:"documentation_url_optional",custom_icon:"custom_icon_optional",nfc_tag_id:"nfc_tag_id_optional",responsible_user_id:"responsible_user",entity_slug:"entity_slug",entity_id:"entity_id",area_id:"area_id_optional",manufacturer:"manufacturer_optional",model:"model_optional",serial_number:"serial_number_optional",installation_date:"installation_date_optional",warranty_expiry:"warranty_expiry_optional",checklist:"checklist_steps_optional",reason:"reason",feedback:"feedback",cost:"cost",duration:"duration",description:"description_optional",group_name:"name",group_description:"description_optional",environmental_entity:"environmental_entity_optional",environmental_attribute:"environmental_attribute_optional",trigger_above:"trigger_above",trigger_below:"trigger_below",trigger_for_minutes:"trigger_for_minutes"};function c(r,o){let e=s[r];if(!e)return r;let t=a(e,o);return t&&t!==e?t:r}function d(r){let e=r.match(/data\['([^']+)'\]/)?.[1],t;return(t=r.match(/length of value must be at most (\d+)/))?{field:e,rule:"too_long",param:t[1]}:(t=r.match(/length of value must be at least (\d+)/))?{field:e,rule:"too_short",param:t[1]}:(t=r.match(/value must be at most (\S+)/))?{field:e,rule:"value_too_high",param:t[1]}:(t=r.match(/value must be at least (\S+)/))?{field:e,rule:"value_too_low",param:t[1]}:/required key not provided/.test(r)?{field:e,rule:"required"}:(t=r.match(/expected (\w+)/))?{field:e,rule:"wrong_type",param:t[1]}:/value must be one of/.test(r)?{field:e,rule:"invalid_choice"}:/not a valid value/.test(r)?{field:e,rule:"invalid_value"}:{field:e,rule:"unknown"}}function g(r,o,e){if(e=e??a("action_error",o),typeof r=="string")return r;if(typeof r!="object"||r===null)return e;let t=r,_=t.message||t.error?.message||"";if(!_)return e;let i=d(_),l=i.field?c(i.field,o):"",n=u=>a(u,o).replace("{field}",l).replace("{n}",i.param??"");switch(i.rule){case"too_long":return n("err_too_long");case"too_short":return n("err_too_short");case"value_too_high":return n("err_value_too_high");case"value_too_low":return n("err_value_too_low");case"required":return n("err_required");case"wrong_type":return n("err_wrong_type").replace("{type}",i.param??"");case"invalid_choice":return n("err_invalid_choice");case"invalid_value":return n("err_invalid_value");default:return _||e}}export{g as a};
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.56.0 */
var o=["notes","cost","duration","photo","user"],t={notes:"notes_label",cost:"cost",duration:"duration",photo:"photo_label",user:"user_label"};export{o as a,t as b};
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.56.0 */
import{n as u}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-C5W5B43R.js";function _(e){return`${e.entry_id??""}\0${e.part_id}`}function l(e,r,s,c){let t=!!e.entry_id&&e.entry_id!==r,a=t?e.entry_id:r,o=s.find(p=>p.entry_id===a),n=(o?.parts||[]).find(p=>p.id===e.part_id)||null,d=t&&o?.object?.name||"",i=n?.name||u("shared_part_unknown",c);return{part:n,foreign:t,ownerName:d,label:d?`${i} (${d})`:i}}function f(e,r,s,c){let{part:t,label:a}=l(e,r,s,c),o=t&&t.stock!==null&&t.stock!==void 0?` (${t.stock}${t.unit?" "+t.unit:""})`:"",n=t?.storage_location?` \u2014 ${t.storage_location}`:"";return`${e.quantity}\xD7 ${a}${o}${n}`}function g(e,r,s,c){let a=(s.find(n=>n.entry_id===r)?.parts||[]).map(n=>({...n})),o=new Set(a.map(n=>_({part_id:n.id})));for(let n of e?.consumes_parts||[]){if(!n.entry_id||n.entry_id===r)continue;let d=_(n);if(o.has(d))continue;o.add(d);let{part:i,ownerName:p}=l(n,r,s,c);a.push({id:n.part_id,name:i?.name||u("shared_part_unknown",c),unit:i?.unit,stock:i?.stock??null,storage_location:i?.storage_location,entry_id:n.entry_id,owner_name:p})}return a}export{_ as a,f as b,g as c};
@@ -0,0 +1,54 @@
/*! maintenance_supporter frontend 2.56.0 */
import{a as t,b as a,c as i,f as l,g as p,i as r}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-C5W5B43R.js";var e=class extends p{constructor(){super(...arguments);this.label="";this.value="";this.placeholder="";this.type="text";this.required=!1;this.disabled=!1}_onInput(n){let o=n.target.value;this.value=o,this.dispatchEvent(new CustomEvent("input",{bubbles:!0,composed:!0,detail:{value:o}}))}render(){return i`
<label class="field">
${this.label?i`<span class="label">${this.label}${this.required?i`<span class="req">*</span>`:l}</span>`:l}
<input
.value=${this.value??""}
.type=${this.type}
?required=${this.required}
?disabled=${this.disabled}
placeholder=${this.placeholder}
step=${this.step??l}
min=${this.min??l}
max=${this.max??l}
pattern=${this.pattern??l}
@input=${this._onInput}
@change=${this._onInput}
/>
${this.helper?i`<span class="helper">${this.helper}</span>`:l}
</label>
`}};e.styles=a`
:host { display: block; }
.field {
display: flex;
flex-direction: column;
gap: 4px;
}
.label {
font-size: 12px;
color: var(--secondary-text-color, #888);
font-weight: 500;
}
.req { color: var(--error-color, #f44336); margin-left: 2px; }
input {
padding: 8px 10px;
font-size: 14px;
background: var(--secondary-background-color, rgba(0,0,0,0.06));
color: var(--primary-text-color);
border: 1px solid var(--divider-color, rgba(255,255,255,0.12));
border-radius: 6px;
font-family: inherit;
width: 100%;
box-sizing: border-box;
outline: none;
}
input:focus {
border-color: var(--primary-color);
}
input:disabled { opacity: 0.5; cursor: not-allowed; }
.helper {
font-size: 11px;
color: var(--secondary-text-color);
font-style: italic;
}
`,t([r()],e.prototype,"label",2),t([r()],e.prototype,"value",2),t([r()],e.prototype,"placeholder",2),t([r()],e.prototype,"type",2),t([r({type:Boolean})],e.prototype,"required",2),t([r({type:Boolean})],e.prototype,"disabled",2),t([r()],e.prototype,"step",2),t([r()],e.prototype,"min",2),t([r()],e.prototype,"max",2),t([r()],e.prototype,"pattern",2),t([r()],e.prototype,"helper",2);customElements.get("ms-textfield")||customElements.define("ms-textfield",e);
@@ -0,0 +1,292 @@
/*! maintenance_supporter frontend 2.56.0 */
import{b as m}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-NGMG4DEY.js";import{a as _}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-SD6IEJBA.js";import{a as g}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-LJXSDCLS.js";import{a as s,b,c as a,f as d,g as v,i as n,j as l,n as r,x as k}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-C5W5B43R.js";var i=class extends v{constructor(){super(...arguments);this.entryId="";this.taskId="";this.taskName="";this.lang="en";this.checklist=[];this.adaptiveEnabled=!1;this.taskType="";this.readingUnit="";this.restockDefault=null;this.restockUnitCost=null;this.currencySymbol="";this.parts=[];this.consumesParts=[];this.consumesInfo=[];this.requiredFields=[];this._open=!1;this._notes="";this._cost="";this._duration="";this._loading=!1;this._error="";this._checklistState={};this._feedback="needed";this._photoDocId="";this._photoPreview="";this._photoUploading=!1;this._readingValue="";this._restockQty="";this._usedParts={};this.checklistPrefill={}}open(){this._open||(this._open=!0,this._notes="",this._cost="",this._duration="",this._error="",this._checklistState=Object.fromEntries(this.checklist.map((e,t)=>[String(t),!!this.checklistPrefill[e]]).filter(([,e])=>e)),this._feedback="needed",this._photoDocId="",this._photoPreview="",this._photoUploading=!1,this._readingValue="",this._restockQty=this.restockDefault!==null?String(this.restockDefault):"",this._usedParts=Object.fromEntries(this.consumesParts.map(e=>[_(e),{...e}])))}_toggleCheck(e){let t=String(e);this._checklistState={...this._checklistState,[t]:!this._checklistState[t]}}_setFeedback(e){this._feedback=e}async _onPhotoInput(e){let t=e.target,o=t.files?.[0];if(t.value="",!!o){this._photoUploading=!0,this._error="";try{let c=new FormData;c.append("entry_id",this.entryId),c.append("tags","photo"),c.append("file",o,o.name);let p=await fetch("/api/maintenance_supporter/document/upload",{method:"POST",headers:{Authorization:`Bearer ${this.hass.auth?.data?.access_token??""}`},body:c});if(!p.ok){this._error=p.status===413?r("doc_too_large",this.lang):r("doc_upload_failed",this.lang);return}let u=await p.json();u.id&&(this._photoDocId=u.id,this._photoPreview=URL.createObjectURL(o))}catch{this._error=r("doc_upload_failed",this.lang)}finally{this._photoUploading=!1}}}_removePhoto(){this._photoPreview&&URL.revokeObjectURL(this._photoPreview),this._photoDocId="",this._photoPreview=""}async _complete(){this._loading=!0,this._error="";try{let e={type:"maintenance_supporter/task/complete",entry_id:this.entryId,task_id:this.taskId};if(this._notes&&(e.notes=this._notes),this._cost){let t=parseFloat(this._cost);!isNaN(t)&&t>=0&&(e.cost=t)}if(this._duration){let t=parseInt(this._duration,10);!isNaN(t)&&t>=0&&(e.duration=t)}if(this.checklist.length>0&&(e.checklist_state=this._checklistState),this.adaptiveEnabled&&(e.feedback=this._feedback),this._photoDocId&&(e.photo_doc_id=this._photoDocId),this._readingValue!==""){let t=parseFloat(this._readingValue);isNaN(t)||(e.reading_value=t)}if(this.restockDefault!==null&&this._restockQty!==""){let t=parseFloat(this._restockQty);!isNaN(t)&&t>=1&&(e.restock_quantity=t)}this.parts.length>0&&(e.used_parts=Object.values(this._usedParts).filter(t=>Number.isFinite(t.quantity)&&t.quantity>0).map(t=>t.entry_id?{part_id:t.part_id,quantity:t.quantity,entry_id:t.entry_id}:{part_id:t.part_id,quantity:t.quantity})),await this.hass.connection.sendMessagePromise(e),this._open=!1,this.dispatchEvent(new CustomEvent("task-completed"))}catch(e){this._error=g(e,this.lang,r("save_error",this.lang))}finally{this._loading=!1}}get _missingRequired(){let e={notes:this._notes.trim()!=="",cost:this._cost.trim()!=="",duration:this._duration.trim()!=="",photo:this._photoDocId!=="",user:!!this.hass?.user};return this.requiredFields.filter(t=>!e[t])}_req(e){return this.requiredFields.includes(e)?a`<span class="req-mark" aria-hidden="true">*</span>`:d}_partsCostSuggestion(){if(this.restockDefault!==null){let o=parseFloat(this._restockQty);return this.restockUnitCost==null||!Number.isFinite(o)||o<=0?null:Math.round(this.restockUnitCost*o*100)/100}if(!this.parts.length)return null;let e=0,t=!1;for(let o of Object.values(this._usedParts)){let c=this.parts.find(p=>_({part_id:p.id,entry_id:p.entry_id})===_(o));c?.cost!=null&&(e+=c.cost*(o.quantity||1),t=!0)}return t?Math.round(e*100)/100:null}_renderCostSuggestion(e){if(this._cost.trim()!=="")return d;let t=this._partsCostSuggestion();if(t==null||t<=0)return d;let o=`${t.toFixed(2)}${this.currencySymbol?` ${this.currencySymbol}`:""}`;return a`<button
type="button"
class="cost-suggestion"
@click=${()=>this._cost=t.toFixed(2)}
>${r("cost_from_parts",e).replace("{amount}",o)}</button>`}_close(){this._open=!1}render(){if(!this._open)return a``;let e=this.lang||this.hass?.language||"en";return a`
<ha-dialog open @closed=${this._close}>
<div class="dialog-title">${r("complete_title",e)}${this.taskName}</div>
<div class="content">
${this._error?a`<div class="error">${this._error}</div>`:d}
${this.checklist.length>0?a`
<div class="checklist-section">
<label class="checklist-label">${r("checklist",e)}</label>
${this.checklist.map((t,o)=>a`
<label class="checklist-item" @click=${()=>this._toggleCheck(o)}>
<input type="checkbox" .checked=${!!this._checklistState[String(o)]} />
<span>${t}</span>
</label>
`)}
</div>
`:d}
${this.taskType==="reading"?a`
<label class="field">
<span class="field-label">${r("reading_value_label",e)}${this.readingUnit?` (${this.readingUnit})`:""}</span>
<input type="number" step="any" class="field-input"
.value=${this._readingValue}
@input=${t=>this._readingValue=t.target.value} />
</label>`:d}
${this.parts.length?a`<div class="used-parts">
<span class="field-label">${r("complete_parts_used",e)}</span>
${this.parts.map(t=>{let o=_({part_id:t.id,entry_id:t.entry_id}),c=this._usedParts[o],p=c!==void 0,u=t.entry_id?{part_id:t.id,quantity:1,entry_id:t.entry_id}:{part_id:t.id,quantity:1};return a`<div class="used-part-row">
<label class="used-part-check">
<input type="checkbox" .checked=${p}
@change=${f=>{let h={...this._usedParts};f.target.checked?h[o]=h[o]||u:delete h[o],this._usedParts=h}} />
<span
>${t.name}${t.owner_name?a`<span class="used-part-owner"> (${t.owner_name})</span>`:d}${t.stock!==null&&t.stock!==void 0?` (${t.stock}${t.unit?" "+t.unit:""})`:""}</span
>
</label>
${p?a`<input class="used-part-qty" type="number" min="0.01" max="999" step="0.01"
.value=${String(c.quantity)}
@input=${f=>{let h=parseFloat(f.target.value);this._usedParts={...this._usedParts,[o]:{...u,quantity:Number.isFinite(h)&&h>=.01?h:1}}}} />`:d}
</div>`})}
</div>`:this.consumesInfo.length?a`<div class="consumes-hint">
${this.consumesInfo.map(t=>a`<div>${t}</div>`)}
</div>`:d}
${this.restockDefault!==null?a`
<label class="field">
<span class="field-label">${r("restock_quantity_label",e)}</span>
<input type="number" step="0.01" min="0.01" class="field-input"
.value=${this._restockQty}
@input=${t=>this._restockQty=t.target.value} />
</label>`:d}
<!-- Native <input>s rather than <ha-textfield>: when this dialog
is opened from a Lovelace card via dialog-mount, ha-textfield
isn't yet registered (HA loads it lazily when its own panels
need it) so the elements render with zero height and the user
only sees the title + Cancel/Complete buttons the original
bug report. Native inputs always render. -->
<label class="field">
<span class="field-label">${r("notes_optional",e)}${this._req("notes")}</span>
<input type="text" class="field-input"
.value=${this._notes}
@input=${t=>this._notes=t.target.value} />
</label>
<label class="field">
<span class="field-label">${r("cost_optional",e)}${this._req("cost")}</span>
<input type="number" step="0.01" min="0" class="field-input"
.value=${this._cost}
@input=${t=>this._cost=t.target.value} />
${this._renderCostSuggestion(e)}
</label>
<label class="field">
<span class="field-label">${r("duration_minutes",e)}${this._req("duration")}</span>
<input type="number" step="0.01" min="0" class="field-input"
.value=${this._duration}
@input=${t=>this._duration=t.target.value} />
</label>
<div class="field">
<span class="field-label">${r("completion_photo_optional",e)}${this._req("photo")}</span>
${this._photoPreview?a`
<div class="photo-preview">
<img src=${this._photoPreview} alt="" />
<button type="button" class="photo-remove" @click=${this._removePhoto}
title="${r("remove",e)}"></button>
</div>`:a`
<label class="photo-pick">
<ha-icon icon="mdi:camera"></ha-icon>
<span>${this._photoUploading?r("uploading",e):r("add_photo",e)}</span>
<input type="file" accept="image/*" capture="environment"
?disabled=${this._photoUploading}
@change=${this._onPhotoInput} />
</label>`}
</div>
${this.adaptiveEnabled?a`
<div class="feedback-section">
<label class="feedback-label">${r("was_maintenance_needed",e)}</label>
<div class="feedback-buttons">
<button
class="feedback-btn ${this._feedback==="needed"?"selected":""}"
@click=${()=>this._setFeedback("needed")}
>${r("feedback_needed",e)}</button>
<button
class="feedback-btn ${this._feedback==="not_needed"?"selected":""}"
@click=${()=>this._setFeedback("not_needed")}
>${r("feedback_not_needed",e)}</button>
<button
class="feedback-btn ${this._feedback==="not_sure"?"selected":""}"
@click=${()=>this._setFeedback("not_sure")}
>${r("feedback_not_sure",e)}</button>
</div>
</div>
`:d}
</div>
<div class="dialog-actions">
<ha-button appearance="plain" @click=${this._close}>
${r("cancel",e)}
</ha-button>
<ha-button
@click=${this._complete}
.disabled=${this._loading||this._missingRequired.length>0}
title=${this._missingRequired.length?this._missingRequired.map(t=>r("err_required",e).replace("{field}",r(m[t]??t,e))).join(" \xB7 "):""}
>
${this._loading?r("completing",e):r("complete",e)}
</ha-button>
</div>
</ha-dialog>
`}};i.styles=[k,b`
.req-mark {
color: var(--error-color, #f44336);
margin-left: 2px;
font-weight: 600;
}
/* #104: one-click cost suggestion from parts — quiet link-style chip. */
.cost-suggestion {
align-self: flex-start;
margin-top: 4px;
padding: 0;
border: none;
background: none;
color: var(--primary-color);
font-size: 12.5px;
cursor: pointer;
text-decoration: underline dotted;
text-underline-offset: 2px;
}
.dialog-title {
font-size: 18px;
font-weight: 500;
padding-bottom: 12px;
}
.content {
display: flex;
flex-direction: column;
gap: 16px;
min-width: 300px;
}
.dialog-actions {
display: flex;
justify-content: flex-end;
gap: 8px;
padding-top: 16px;
}
.consumes-hint {
font-size: 13px;
color: var(--secondary-text-color);
border-left: 3px solid var(--primary-color);
padding: 4px 8px;
margin: 4px 0 8px;
}
/* #99: editable per-completion parts selection */
.used-parts { margin: 4px 0 8px; display: flex; flex-direction: column; gap: 4px; }
.used-part-row { display: flex; align-items: center; gap: 8px; }
.used-part-check {
display: flex; align-items: center; gap: 6px; flex: 1;
font-size: 13px; cursor: pointer;
}
.used-part-check input { cursor: pointer; }
/* #111: whose stock this row draws on. Muted but never omitted an
unlabelled foreign pool is indistinguishable from an own part. */
.used-part-owner { color: var(--secondary-text-color); }
.used-part-qty {
width: 76px; padding: 4px 6px; border-radius: 4px; font: inherit; font-size: 13px;
border: 1px solid var(--divider-color);
background: var(--card-background-color);
color: var(--primary-text-color);
}
.error {
color: var(--error-color, #f44336);
font-size: 13px;
}
/* .field/.field-label/.field-input come from nativeFieldStyles */
.photo-pick {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
border: 1px dashed var(--divider-color);
border-radius: 8px;
cursor: pointer;
font-size: 13px;
color: var(--secondary-text-color);
width: fit-content;
}
.photo-pick:hover { border-color: var(--primary-color); }
.photo-pick input[type="file"] { display: none; }
.photo-preview {
position: relative;
width: fit-content;
}
.photo-preview img {
max-width: 160px;
max-height: 160px;
border-radius: 8px;
display: block;
}
.photo-remove {
position: absolute;
top: -8px;
right: -8px;
width: 24px;
height: 24px;
border-radius: 50%;
border: none;
background: var(--error-color, #db4437);
color: #fff;
cursor: pointer;
font-size: 12px;
line-height: 1;
}
.checklist-section {
display: flex;
flex-direction: column;
gap: 8px;
padding: 8px 0;
border-bottom: 1px solid var(--divider-color);
margin-bottom: 4px;
}
.checklist-label {
font-weight: 500;
font-size: 13px;
color: var(--secondary-text-color);
}
.checklist-item {
display: flex;
align-items: center;
gap: 8px;
cursor: pointer;
padding: 4px 0;
font-size: 14px;
}
.checklist-item input[type="checkbox"] {
width: 18px;
height: 18px;
cursor: pointer;
}
.feedback-section {
display: flex;
flex-direction: column;
gap: 8px;
padding: 8px 0;
border-top: 1px solid var(--divider-color);
}
.feedback-label {
font-weight: 500;
font-size: 13px;
color: var(--secondary-text-color);
}
.feedback-buttons {
display: flex;
gap: 8px;
}
.feedback-btn {
flex: 1;
padding: 8px 12px;
border: 1px solid var(--divider-color);
border-radius: 8px;
background: var(--card-background-color, #fff);
color: var(--primary-text-color);
font-size: 13px;
cursor: pointer;
text-align: center;
transition: all 0.2s;
}
.feedback-btn:hover {
background: var(--secondary-background-color, #f5f5f5);
}
.feedback-btn.selected {
background: var(--primary-color);
color: var(--text-primary-color, #fff);
border-color: var(--primary-color);
}
`],s([n({attribute:!1})],i.prototype,"hass",2),s([n()],i.prototype,"entryId",2),s([n()],i.prototype,"taskId",2),s([n()],i.prototype,"taskName",2),s([n()],i.prototype,"lang",2),s([n({type:Array})],i.prototype,"checklist",2),s([n({type:Boolean})],i.prototype,"adaptiveEnabled",2),s([n()],i.prototype,"taskType",2),s([n()],i.prototype,"readingUnit",2),s([n({attribute:!1})],i.prototype,"restockDefault",2),s([n({attribute:!1})],i.prototype,"restockUnitCost",2),s([n()],i.prototype,"currencySymbol",2),s([n({attribute:!1})],i.prototype,"parts",2),s([n({attribute:!1})],i.prototype,"consumesParts",2),s([n({type:Array})],i.prototype,"consumesInfo",2),s([n({type:Array})],i.prototype,"requiredFields",2),s([l()],i.prototype,"_open",2),s([l()],i.prototype,"_notes",2),s([l()],i.prototype,"_cost",2),s([l()],i.prototype,"_duration",2),s([l()],i.prototype,"_loading",2),s([l()],i.prototype,"_error",2),s([l()],i.prototype,"_checklistState",2),s([l()],i.prototype,"_feedback",2),s([l()],i.prototype,"_photoDocId",2),s([l()],i.prototype,"_photoPreview",2),s([l()],i.prototype,"_photoUploading",2),s([l()],i.prototype,"_readingValue",2),s([l()],i.prototype,"_restockQty",2),s([l()],i.prototype,"_usedParts",2),s([n({attribute:!1})],i.prototype,"checklistPrefill",2);customElements.get("maintenance-complete-dialog")||customElements.define("maintenance-complete-dialog",i);export{i as MaintenanceCompleteDialog};
@@ -0,0 +1,143 @@
/*! maintenance_supporter frontend 2.56.0 */
import"/maintenance_supporter_panelfiles/panel-chunks/chunk-ZK3W7TF6.js";import{a as h}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-LJXSDCLS.js";import{a as r,b as _,c as l,f as o,g as p,i as d,j as s,n as i}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-C5W5B43R.js";var e=class extends p{constructor(){super(...arguments);this.objects=[];this._open=!1;this._loading=!1;this._error="";this._name="";this._manufacturer="";this._model="";this._serialNumber="";this._areaId="";this._installationDate="";this._warrantyExpiry="";this._documentationUrl="";this._notes="";this._haDeviceId="";this._parentEntryId="";this._entryId=null}get _lang(){return this.hass?.language??navigator.language.split("-")[0]??"en"}openCreate(){this._entryId=null,this._name="",this._manufacturer="",this._model="",this._serialNumber="",this._areaId="",this._installationDate="",this._warrantyExpiry="",this._documentationUrl="",this._notes="",this._haDeviceId="",this._parentEntryId="",this._error="",this._open=!0}openEdit(a,n){this._entryId=a,this._name=n.name||"",this._manufacturer=n.manufacturer||"",this._model=n.model||"",this._serialNumber=n.serial_number||"",this._areaId=n.area_id||"",this._installationDate=n.installation_date||"",this._warrantyExpiry=n.warranty_expiry||"",this._documentationUrl=n.documentation_url||"",this._notes=n.notes||"",this._haDeviceId=n.ha_device_id||"",this._parentEntryId=n.parent_entry_id||"",this._error="",this._open=!0}async _save(){if(!this._loading&&this._name.trim()){this._loading=!0,this._error="";try{this._entryId?await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object/update",entry_id:this._entryId,name:this._name,manufacturer:this._manufacturer||null,model:this._model||null,serial_number:this._serialNumber||null,area_id:this._areaId||null,installation_date:this._installationDate||null,warranty_expiry:this._warrantyExpiry||null,documentation_url:this._documentationUrl.trim()||null,notes:this._notes.trim()||null,ha_device_id:this._haDeviceId||null,parent_entry_id:this._parentEntryId||null}):await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/object/create",name:this._name,manufacturer:this._manufacturer||null,model:this._model||null,serial_number:this._serialNumber||null,area_id:this._areaId||null,installation_date:this._installationDate||null,warranty_expiry:this._warrantyExpiry||null,documentation_url:this._documentationUrl.trim()||null,notes:this._notes.trim()||null,ha_device_id:this._haDeviceId||null,parent_entry_id:this._parentEntryId||null}),this._open=!1,this.dispatchEvent(new CustomEvent("object-saved"))}catch(a){this._error=h(a,this._lang,i("save_error",this._lang))}finally{this._loading=!1}}}_parentChoices(){return(this.objects||[]).filter(a=>a.entry_id!==this._entryId)}_close(){this._open=!1}render(){if(!this._open)return l``;let a=this._lang,n=this._entryId?i("edit_object",a):i("new_object",a);return l`
<ha-dialog open @closed=${this._close}>
<div class="dialog-title">${n}</div>
<div class="content">
${this._error?l`<div class="error">${this._error}</div>`:o}
<ms-textfield
label="${i("name",a)}"
required
.value=${this._name}
@input=${t=>this._name=t.target.value}
></ms-textfield>
<ms-textfield
label="${i("manufacturer_optional",a)}"
.value=${this._manufacturer}
@input=${t=>this._manufacturer=t.target.value}
></ms-textfield>
<ms-textfield
label="${i("model_optional",a)}"
.value=${this._model}
@input=${t=>this._model=t.target.value}
></ms-textfield>
<ms-textfield
label="${i("serial_number_optional",a)}"
.value=${this._serialNumber}
@input=${t=>this._serialNumber=t.target.value}
></ms-textfield>
<ms-textfield
label="${i("documentation_url_optional",a)}"
type="url"
.value=${this._documentationUrl}
@input=${t=>this._documentationUrl=t.target.value}
></ms-textfield>
<ha-area-picker
.hass=${this.hass}
label="${i("area_id_optional",a)}"
.value=${this._areaId}
@value-changed=${t=>this._areaId=t.detail.value||""}
></ha-area-picker>
<ms-textfield
label="${i("installation_date_optional",a)}"
type="date"
.value=${this._installationDate}
@input=${t=>this._installationDate=t.target.value}
></ms-textfield>
<ms-textfield
label="${i("warranty_expiry_optional",a)}"
type="date"
.value=${this._warrantyExpiry}
@input=${t=>this._warrantyExpiry=t.target.value}
></ms-textfield>
<ha-form
.hass=${this.hass}
.data=${{device:this._haDeviceId||void 0}}
.schema=${[{name:"device",selector:{device:{}}}]}
.computeLabel=${()=>i("link_device_optional",a)}
@value-changed=${t=>this._haDeviceId=t.detail.value?.device||""}
></ha-form>
${this._parentChoices().length?l`<label class="textarea-field">
<span class="textarea-label">${i("parent_object_optional",a)}</span>
<select
class="parent-select"
.value=${this._parentEntryId}
@change=${t=>this._parentEntryId=t.target.value}
>
<option value="" ?selected=${!this._parentEntryId}>
${i("parent_none",a)}
</option>
${this._parentChoices().map(t=>l`<option
value=${t.entry_id}
?selected=${this._parentEntryId===t.entry_id}
>${t.object.name}</option>`)}
</select>
</label>`:o}
<label class="textarea-field">
<span class="textarea-label">${i("object_notes_optional",a)}</span>
<textarea
rows="3"
.value=${this._notes}
@input=${t=>this._notes=t.target.value}
></textarea>
</label>
</div>
<div class="dialog-actions">
<ha-button appearance="plain" @click=${this._close}>
${i("cancel",this._lang)}
</ha-button>
<ha-button
@click=${this._save}
.disabled=${this._loading||!this._name.trim()}
>
${this._loading?i("saving",this._lang):i("save",this._lang)}
</ha-button>
</div>
</ha-dialog>
`}};e.styles=_`
.dialog-title {
font-size: 18px;
font-weight: 500;
padding-bottom: 12px;
}
.content {
display: flex;
flex-direction: column;
gap: 16px;
min-width: 300px;
}
.dialog-actions {
display: flex;
justify-content: flex-end;
gap: 8px;
padding-top: 16px;
}
ms-textfield {
display: block;
}
.textarea-field {
display: flex; flex-direction: column; gap: 4px;
}
.textarea-label {
font-size: 12px; color: var(--secondary-text-color, #888); font-weight: 500;
}
.textarea-field textarea {
padding: 8px 10px; font-size: 14px; font-family: inherit;
background: var(--secondary-background-color, rgba(0,0,0,0.06));
color: var(--primary-text-color);
border: 1px solid var(--divider-color); border-radius: 6px;
resize: vertical;
}
.textarea-field textarea:focus {
outline: none; border-color: var(--primary-color);
}
.parent-select {
padding: 8px 10px; font-size: 14px; font-family: inherit;
background: var(--secondary-background-color, rgba(0,0,0,0.06));
color: var(--primary-text-color);
border: 1px solid var(--divider-color); border-radius: 6px;
}
.error {
color: var(--error-color, #f44336);
font-size: 13px;
}
`,r([d({attribute:!1})],e.prototype,"hass",2),r([d({attribute:!1})],e.prototype,"objects",2),r([s()],e.prototype,"_open",2),r([s()],e.prototype,"_loading",2),r([s()],e.prototype,"_error",2),r([s()],e.prototype,"_name",2),r([s()],e.prototype,"_manufacturer",2),r([s()],e.prototype,"_model",2),r([s()],e.prototype,"_serialNumber",2),r([s()],e.prototype,"_areaId",2),r([s()],e.prototype,"_installationDate",2),r([s()],e.prototype,"_warrantyExpiry",2),r([s()],e.prototype,"_documentationUrl",2),r([s()],e.prototype,"_notes",2),r([s()],e.prototype,"_haDeviceId",2),r([s()],e.prototype,"_parentEntryId",2),r([s()],e.prototype,"_entryId",2);customElements.get("maintenance-object-dialog")||customElements.define("maintenance-object-dialog",e);export{e as MaintenanceObjectDialog};
@@ -0,0 +1,213 @@
/*! maintenance_supporter frontend 2.56.0 */
import{a,b as v,c as n,f as g,g as b,i as m,j as c,n as t}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-C5W5B43R.js";function p(l){return l.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;")}function x(l){return!l.startsWith("data:image/svg+xml,")&&!l.startsWith("data:image/png;base64,")?"":p(l)}function $(l){return l.replace(/[/\\:*?"<>|#%]+/g,"").replace(/\s+/g,"-").toLowerCase().substring(0,100)}var r=class extends b{constructor(){super(...arguments);this.lang="en";this._open=!1;this._loading=!1;this._error="";this._viewResult=null;this._completeResult=null;this._urlMode="companion";this._entryId="";this._taskId=null;this._objectName="";this._taskName="";this._generateSeq=0}openForObject(e,i){this._entryId=e,this._taskId=null,this._objectName=i,this._taskName="",this._urlMode="companion",this._error="",this._viewResult=null,this._completeResult=null,this._open=!0,this._generate()}openForTask(e,i,o,s){this._entryId=e,this._taskId=i,this._objectName=o,this._taskName=s,this._urlMode="companion",this._error="",this._viewResult=null,this._completeResult=null,this._open=!0,this._generate()}async _generate(){let e=++this._generateSeq;this._loading=!0,this._error="",this._viewResult=null,this._completeResult=null;try{let i={type:"maintenance_supporter/qr/generate",entry_id:this._entryId,url_mode:this._urlMode};this._taskId&&(i.task_id=this._taskId);let o=[this.hass.connection.sendMessagePromise({...i,action:"view"})];this._taskId&&o.push(this.hass.connection.sendMessagePromise({...i,action:"complete"}));let s=await Promise.all(o);if(e!==this._generateSeq)return;this._viewResult=s[0],s.length>1&&(this._completeResult=s[1])}catch(i){if(e!==this._generateSeq)return;let o=i?.code,s=i?.message;this._error=o==="no_url"||typeof s=="string"&&s.includes("No Home Assistant URL")?t("qr_error_no_url",this.lang):t("qr_error",this.lang)}finally{e===this._generateSeq&&(this._loading=!1)}}_setUrlMode(e){this._urlMode!==e&&(this._urlMode=e,this._generate())}_print(){if(!this._viewResult)return;let e=this._viewResult,i=e.label.task_name?`${e.label.object_name} \u2014 ${e.label.task_name}`:e.label.object_name,o=[e.label.manufacturer,e.label.model].filter(Boolean).join(" "),s=window.open("","_blank","width=600,height=500");if(!s)return;let h=this.lang||"en",d=p(i),u=p(o),_=!!this._completeResult,f=p(t("qr_action_view",h)),w=p(t("qr_action_complete",h));s.document.write(`<!DOCTYPE html><html><head><meta charset="utf-8">
<meta name="color-scheme" content="light">
<title>${d}</title>
<style>
/* Printable sheet \u2014 must not inherit the phone's dark theme. The QR images
carry their own white quiet zone and stay scannable either way, but the
labels below are explicit dark greys and would vanish on a WebView's dark
canvas. Same reasoning as helpers/report.ts. */
:root{color-scheme:light}
body{font-family:sans-serif;text-align:center;padding:20px;background:#fff;color:#1a1a1a}
h2{margin:0 0 4px}
.sub{color:#666;font-size:14px;margin-bottom:16px}
.qr-row{display:flex;justify-content:center;gap:24px;margin:12px 0}
.qr-col{display:flex;flex-direction:column;align-items:center;gap:6px}
.qr-col img{width:${_?"200px":"280px"}}
.qr-label{font-size:13px;font-weight:500;color:#333}
.url{font-size:10px;color:#999;word-break:break-all;margin-top:8px;max-width:480px}
</style></head><body>
<h2>${d}</h2>
${u?`<div class="sub">${u}</div>`:""}
<div class="qr-row">
<div class="qr-col">
<img src="${x(this._viewResult.svg_data_uri)}" alt="QR Info" />
<div class="qr-label">${f}</div>
</div>
${_?`<div class="qr-col">
<img src="${x(this._completeResult.svg_data_uri)}" alt="QR Complete" />
<div class="qr-label">${w}</div>
</div>`:""}
</div>
<div class="url">${p(this._viewResult.url)}</div>
<script>setTimeout(()=>window.print(),300)<\/script>
</body></html>`),s.document.close()}_downloadSvg(e,i){let o=decodeURIComponent(e.svg_data_uri.replace("data:image/svg+xml,","")),s=new Blob([o],{type:"image/svg+xml"}),h=URL.createObjectURL(s),d=document.createElement("a");d.href=h;let u=this._taskName?`${this._objectName}-${this._taskName}`:this._objectName;d.download=`qr-${$(u)}-${i}.svg`,d.click(),URL.revokeObjectURL(h)}_close(){this._open=!1,this._viewResult=null,this._completeResult=null,this._error="",this._loading=!1}render(){if(!this._open)return n``;let e=this.lang||this.hass?.language||"en",i=this._taskName?`${t("qr_code",e)}: ${this._objectName} \u2014 ${this._taskName}`:`${t("qr_code",e)}: ${this._objectName}`,o=!!this._viewResult;return n`
<ha-dialog open @closed=${this._close}>
<div class="dialog-title">${i}</div>
<div class="content">
${this._loading?n`<div class="loading">${t("qr_generating",e)}</div>`:this._error?n`<div class="error">${this._error}</div>`:o?n`
<div class="qr-pair">
<div class="qr-item">
<img
class="qr-image ${this._completeResult?"small":""}"
src="${this._viewResult.svg_data_uri}"
alt="QR Info"
/>
<div class="qr-item-label">${t("qr_action_view",e)}</div>
<button class="dl-btn"
@click=${()=>this._downloadSvg(this._viewResult,"info")}>
<ha-icon icon="mdi:download"></ha-icon>
${t("qr_download",e)}
</button>
</div>
${this._completeResult?n`
<div class="qr-item">
<img
class="qr-image small"
src="${this._completeResult.svg_data_uri}"
alt="QR Complete"
/>
<div class="qr-item-label">${t("qr_action_complete",e)}</div>
<button class="dl-btn"
@click=${()=>this._downloadSvg(this._completeResult,"complete")}>
<ha-icon icon="mdi:download"></ha-icon>
${t("qr_download",e)}
</button>
</div>
`:g}
</div>
<div class="url-display">${this._viewResult.url}</div>
`:g}
<div class="action-row">
<label>${t("qr_url_mode",e)}</label>
<div class="action-toggle">
<button class="toggle-btn ${this._urlMode==="companion"?"active":""}"
@click=${()=>this._setUrlMode("companion")}>${t("qr_mode_companion",e)}</button>
<button class="toggle-btn ${this._urlMode==="local"?"active":""}"
@click=${()=>this._setUrlMode("local")}>${t("qr_mode_local",e)}</button>
<button class="toggle-btn ${this._urlMode==="server"?"active":""}"
@click=${()=>this._setUrlMode("server")}>${t("qr_mode_server",e)}</button>
</div>
</div>
</div>
<div class="dialog-actions">
<ha-button appearance="plain" @click=${this._close}>
${t("cancel",e)}
</ha-button>
<ha-button
@click=${this._print}
.disabled=${!o}
>
${t("qr_print",e)}
</ha-button>
</div>
</ha-dialog>
`}};r.styles=v`
.dialog-title {
font-size: 18px;
font-weight: 500;
padding-bottom: 12px;
}
.content {
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
min-width: 300px;
}
.dialog-actions {
display: flex;
justify-content: flex-end;
gap: 8px;
padding-top: 16px;
}
.qr-pair {
display: flex;
gap: 20px;
justify-content: center;
width: 100%;
}
.qr-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
}
.qr-image {
width: 240px;
height: 240px;
image-rendering: pixelated;
}
.qr-image.small {
width: 180px;
height: 180px;
}
.qr-item-label {
font-size: 12px;
font-weight: 500;
color: var(--secondary-text-color);
text-align: center;
}
.dl-btn {
display: inline-flex;
align-items: center;
gap: 6px;
background: none;
border: 1px solid var(--divider-color, #e0e0e0);
cursor: pointer;
font-size: 13px;
color: var(--primary-text-color);
padding: 6px 14px;
border-radius: 18px;
transition: background 0.2s, border-color 0.2s;
}
.dl-btn:hover {
background: var(--secondary-background-color, #f5f5f5);
border-color: var(--primary-color);
}
.dl-btn ha-icon {
--mdc-icon-size: 18px;
}
.url-display {
font-size: 11px;
color: var(--secondary-text-color);
word-break: break-all;
text-align: center;
max-width: 400px;
}
.loading {
padding: 40px 0;
color: var(--secondary-text-color);
}
.error {
padding: 20px 0;
color: var(--error-color, #f44336);
}
.action-row {
display: flex;
flex-direction: column;
gap: 6px;
width: 100%;
}
.action-row label {
font-size: 13px;
color: var(--secondary-text-color);
}
.action-toggle {
display: flex;
gap: 4px;
background: var(--divider-color, #e0e0e0);
border-radius: 6px;
padding: 3px;
}
.toggle-btn {
flex: 1;
padding: 8px 12px;
border: none;
background: transparent;
color: var(--primary-text-color);
cursor: pointer;
border-radius: 4px;
font-size: 13px;
transition: all 0.2s;
line-height: 1.3;
}
.toggle-btn:hover {
background: rgba(0, 0, 0, 0.05);
}
.toggle-btn.active {
background: var(--primary-color);
color: var(--text-primary-color, #fff);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
}
`,a([m({attribute:!1})],r.prototype,"hass",2),a([m()],r.prototype,"lang",2),a([c()],r.prototype,"_open",2),a([c()],r.prototype,"_loading",2),a([c()],r.prototype,"_error",2),a([c()],r.prototype,"_viewResult",2),a([c()],r.prototype,"_completeResult",2),a([c()],r.prototype,"_urlMode",2);customElements.get("maintenance-qr-dialog")||customElements.define("maintenance-qr-dialog",r);export{r as MaintenanceQrDialog};
@@ -0,0 +1,146 @@
/*! maintenance_supporter frontend 2.56.0 */
import{a as g}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-LJXSDCLS.js";import{a as l,b as m,c as i,f as h,g as v,i as u,j as d,n as p,p as f}from"/maintenance_supporter_panelfiles/panel-chunks/chunk-C5W5B43R.js";var a=class extends v{constructor(){super(...arguments);this._open=!1;this._loading=!1;this._adopting=!1;this._error="";this._setups=[];this._selected=new Set;this._baselines=new Map;this._targets=new Map;this._objects=[];this._localeReady=!1;this._toggle=t=>{let e=new Set(this._selected);e.has(t)?e.delete(t):e.add(t),this._selected=e};this._adopt=async()=>{if(!(this._selected.size===0||this._adopting)){this._adopting=!0,this._error="";try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/integration_setups/adopt",selections:[...this._selected].map(e=>{let r={device_id:e},c=this._targets.get(e);c&&(r.entry_id=c);let s=this._setups.find(n=>n.device_id===e);for(let n of s?.tasks??[]){let o=this._baselines.get(`${e} ${n.task_name}`),_=o?parseFloat(o):NaN;!isNaN(_)&&_>=0&&((r.baselines??={})[n.task_name]=_)}return r})});this.dispatchEvent(new CustomEvent("integration-setups-adopted",{bubbles:!0,composed:!0,detail:t})),this._open=!1}catch(t){this._error=g(t,this._lang)}finally{this._adopting=!1}}}}get _lang(){return this.hass?.language||"en"}updated(t){t.has("hass")&&this.hass&&!this._localeReady&&(this._localeReady=!0,f(this._lang).then(()=>this.requestUpdate()))}async open(){this._open=!0,this._loading=!0,this._error="",this._setups=[],this._selected=new Set;try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/integration_setups/discover"});this._setups=t.setups||[],this._selected=new Set(this._setups.map(e=>e.device_id)),this._baselines=new Map,this._targets=new Map;try{let e=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/objects"});this._objects=(e.objects||[]).map(r=>({entry_id:r.entry_id,name:r.object?.name||r.entry_id})).sort((r,c)=>r.name.localeCompare(c.name))}catch{this._objects=[]}}catch(t){this._error=g(t,this._lang)}finally{this._loading=!1}}_close(){this._open=!1}render(){if(!this._open)return i``;let t=this._lang;return i`
<div class="overlay" @click=${this._close}>
<div class="card" @click=${e=>e.stopPropagation()}>
<div class="title">${p("setups_title",t)}</div>
<div class="hint">${p("setups_hint",t)}</div>
${this._error?i`<div class="error">${this._error}</div>`:h}
${this._loading?i`<div class="loading">…</div>`:this._setups.length===0?i`<div class="empty">${p("setups_none",t)}</div>`:i`
<div class="list">
${this._setups.map(e=>{let r=this._selected.has(e.device_id),c=[e.integration_name,e.area_name].filter(Boolean).join(" \xB7 ");return i`
<label class="row">
<input
type="checkbox"
.checked=${r}
@change=${()=>this._toggle(e.device_id)}
/>
<div class="row-main">
<div class="row-top">
<span class="row-name">${e.device_name}</span>
</div>
<div class="row-sub">${c}</div>
<div class="row-target" @click=${s=>s.preventDefault()}>
${r&&this._objects.length>0?i`
<select
class="target-select"
@change=${s=>{let n=new Map(this._targets),o=s.target.value;o?n.set(e.device_id,o):n.delete(e.device_id),this._targets=n}}
>
<option value="" ?selected=${!this._targets.get(e.device_id)}>
${e.suggested_entry_id?e.suggested_object_name:p("setups_target_new",t).replace("{name}",e.suggested_object_name)}
</option>
${this._objects.filter(s=>s.entry_id!==e.suggested_entry_id).map(s=>i`<option
value=${s.entry_id}
?selected=${this._targets.get(e.device_id)===s.entry_id}
>
${s.name}
</option>`)}
</select>
`:i`${e.suggested_object_name}${e.suggested_entry_id?h:i` <span class="new-tag">${p("adopt_problem_new_object",t)}</span>`}`}
</div>
<div class="row-tasks">
${e.tasks.map(s=>i`<span class="chip" title=${s.entity_ids.join(", ")}>
<ha-icon icon="mdi:link-variant"></ha-icon>${s.task_name_localized||s.task_name}
</span>`)}
</div>
${r?e.tasks.filter(s=>s.direction==="usage_delta").map(s=>{let n=`${e.device_id} ${s.task_name}`;return i`
<div class="baseline-field" @click=${o=>o.preventDefault()}>
<span class="baseline-label"
>${s.task_name_localized||s.task_name}
${p("setups_baseline_hint",t)}</span
>
<input
type="number"
step="any"
min="0"
.value=${this._baselines.get(n)??""}
@click=${o=>o.preventDefault()}
@input=${o=>{let _=new Map(this._baselines);_.set(n,o.target.value),this._baselines=_}}
/>
</div>
`}):h}
</div>
</label>
`})}
</div>
`}
<div class="actions">
<ha-button appearance="plain" @click=${this._close}>
${p("cancel",t)}
</ha-button>
<ha-button
@click=${this._adopt}
.disabled=${this._selected.size===0||this._adopting}
>
${p("setups_adopt",t)}
</ha-button>
</div>
</div>
</div>
`}};a.styles=m`
.overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.card {
background: var(--card-background-color, #fff);
color: var(--primary-text-color);
border-radius: 12px;
padding: 20px;
display: flex;
flex-direction: column;
gap: 12px;
min-width: min(360px, calc(100vw - 24px));
max-width: 560px;
width: 90vw;
max-height: 80vh;
overflow: hidden;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
}
.title { font-size: 18px; font-weight: 500; }
.hint { color: var(--secondary-text-color); font-size: 13px; }
.error { color: var(--error-color, #f44336); font-size: 13px; }
.loading, .empty { color: var(--secondary-text-color); font-size: 14px; padding: 12px 0; }
.list { display: flex; flex-direction: column; gap: 6px; overflow-y: auto; max-height: 50vh; }
.row {
display: flex; align-items: flex-start; gap: 10px; padding: 8px;
border: 1px solid var(--divider-color); border-radius: 6px; cursor: pointer;
}
.row input { margin-top: 2px; cursor: pointer; }
.row-main { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1; }
.row-name { font-weight: 500; font-size: 13px; }
.row-sub, .row-target { color: var(--secondary-text-color); font-size: 12px; }
.new-tag { font-style: italic; }
.row-tasks { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 2px; }
.chip {
display: inline-flex; align-items: center; gap: 4px;
font-size: 11px; padding: 2px 8px; border-radius: 10px;
background: var(--secondary-background-color, rgba(0, 0, 0, 0.06));
color: var(--primary-text-color); white-space: nowrap;
}
.chip ha-icon { --mdc-icon-size: 12px; color: var(--primary-color); }
.target-select {
font-size: 12px; padding: 2px 4px; max-width: 100%;
border: 1px solid var(--divider-color); border-radius: 4px;
background: var(--card-background-color, #fff);
color: var(--primary-text-color);
}
.baseline-field {
display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
margin-top: 4px; font-size: 12px; color: var(--secondary-text-color);
}
.baseline-field input {
width: 110px; padding: 3px 6px; font-size: 12px;
border: 1px solid var(--divider-color); border-radius: 4px;
background: var(--card-background-color, #fff);
color: var(--primary-text-color);
}
.actions { display: flex; justify-content: flex-end; gap: 8px; padding-top: 8px; }
`,l([u({attribute:!1})],a.prototype,"hass",2),l([d()],a.prototype,"_open",2),l([d()],a.prototype,"_loading",2),l([d()],a.prototype,"_adopting",2),l([d()],a.prototype,"_error",2),l([d()],a.prototype,"_setups",2),l([d()],a.prototype,"_selected",2),l([d()],a.prototype,"_baselines",2),l([d()],a.prototype,"_targets",2),l([d()],a.prototype,"_objects",2);customElements.get("maintenance-suggested-setups-dialog")||customElements.define("maintenance-suggested-setups-dialog",a);export{a as MaintenanceSuggestedSetupsDialog};
File diff suppressed because one or more lines are too long
@@ -0,0 +1,125 @@
/*! maintenance_supporter frontend 2.56.0 */
import{a as v}from"./chunk-XWMJNIQI.js";import{a as u,b as o,d as h,e as b,f as y,g as l,h as g,i as r,k as m,t as p}from"./chunk-TRW3CRLS.js";import{a as n}from"./chunk-UHANDS57.js";var x=80,s=class extends b{constructor(){super(...arguments);this._config={type:""};this._status=null;this._busy=!1;this._error="";this._localMonthly="";this._localYearly="";this._dirty=!1;this._loaded=!1}setConfig(t){this._config=t}getCardSize(){return 2}get _lang(){return this.hass?.language||"en"}get _isAdmin(){return this.hass?.user?.is_admin??!0}updated(t){super.updated(t),t.has("hass")&&this.hass&&!this._loaded&&(this._loaded=!0,this._load(),m(this._lang).then(()=>this.requestUpdate()))}async _load(){try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/budget_status"});this._status=t,this._localMonthly=t.monthly_budget?String(t.monthly_budget):"",this._localYearly=t.yearly_budget?String(t.yearly_budget):"",this._dirty=!1}catch(t){this._error=p(t,this._lang)}}async _save(){if(this._isAdmin){this._busy=!0,this._error="";try{let t=parseFloat(this._localMonthly),a=parseFloat(this._localYearly),i={};!isNaN(t)&&t>=0&&(i.budget_monthly=t),!isNaN(a)&&a>=0&&(i.budget_yearly=a),await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/global/update",settings:i}),await this._load()}catch(t){this._error=p(t,this._lang)}finally{this._busy=!1}}}_onDeepLink(){history.pushState(null,"","/maintenance-supporter?ms_action=open_budget"),window.dispatchEvent(new CustomEvent("location-changed"))}render(){let t=this._lang,a=this._status;if(!a)return o`<ha-card><div class="loading">${r("loading",t)||"Loading\u2026"}</div></ha-card>`;let i=a.currency_symbol||g,_=a.alert_threshold_pct??x,f=[{label:r("budget_monthly",t)||"Monthly",spent:a.monthly_spent||0,budget:a.monthly_budget||0},{label:r("budget_yearly",t)||"Yearly",spent:a.yearly_spent||0,budget:a.yearly_budget||0}];return o`
<ha-card>
<div class="card-content">
<div class="header">
<div class="title">
<span class="emoji">💰</span>
<span>${this._config.title||r("settings_budget",t)||"Budget"}</span>
</div>
<span class="currency">${i}</span>
</div>
${this._error?o`<div class="error">${this._error}</div>`:h}
${f.map(e=>{if(!(e.budget>0))return o`
<div class="track spent-only">
<div class="track-label-row">
<label>${e.label}</label>
<span class="track-numbers ok">${e.spent.toFixed(0)} ${i}</span>
</div>
</div>
`;let d=Math.min(100,Math.max(0,e.spent/e.budget*100)),c=d>=100?"danger":d>=_?"warning":"ok";return o`
<div class="track">
<div class="track-label-row">
<label>${e.label}</label>
<span class="track-numbers ${c}">
${e.spent.toFixed(0)} / ${e.budget.toFixed(0)} ${i}
</span>
</div>
<div class="bar"><div class="bar-fill ${c}" style="width:${d}%"></div></div>
</div>
`})}
${this._isAdmin?o`
<div class="inputs-row">
<div class="input-field">
<label>${r("budget_monthly_set",t)||"Set monthly"}</label>
<div class="input-wrap">
<input type="number" min="0" step="1"
.value=${this._localMonthly}
?disabled=${this._busy}
@input=${e=>{this._localMonthly=e.target.value,this._dirty=!0}} />
<span class="input-suffix">${i}</span>
</div>
</div>
<div class="input-field">
<label>${r("budget_yearly_set",t)||"Set yearly"}</label>
<div class="input-wrap">
<input type="number" min="0" step="1"
.value=${this._localYearly}
?disabled=${this._busy}
@input=${e=>{this._localYearly=e.target.value,this._dirty=!0}} />
<span class="input-suffix">${i}</span>
</div>
</div>
</div>
<div class="actions">
<button class="btn ${this._dirty?"primary":"muted"}"
@click=${this._save}
?disabled=${this._busy||!this._dirty}>
<ha-icon icon="${this._dirty?"mdi:content-save":"mdi:check"}"></ha-icon>
${this._dirty?r("save",t)||"Save":r("saved",t)||"Saved"}
</button>
<button class="btn link" @click=${this._onDeepLink}>
${r("budget_advanced",t)||"Currency, alerts\u2026"}
</button>
</div>
`:o`
<button class="btn link" @click=${this._onDeepLink}>
${r("budget_open_panel",t)||"Open in panel"}
</button>
`}
</div>
</ha-card>
`}};s.styles=[v,u`
.currency {
font-size: 14px; font-weight: 600;
color: var(--secondary-text-color);
background: var(--secondary-background-color);
padding: 2px 10px; border-radius: 999px;
}
.track { display: flex; flex-direction: column; gap: 4px; }
.track-label-row {
display: flex; align-items: center; justify-content: space-between;
}
.track-label-row label {
font-size: 12px; color: var(--secondary-text-color);
text-transform: uppercase; letter-spacing: 0.5px;
}
.track-numbers { font-size: 13px; font-weight: 600; }
.track-numbers.ok { color: var(--primary-text-color); }
.track-numbers.warning { color: #ff9800; }
.track-numbers.danger { color: var(--error-color, #f44336); }
.bar {
height: 6px; background: var(--secondary-background-color);
border-radius: 3px; overflow: hidden;
}
.bar-fill { height: 100%; transition: width 0.3s; border-radius: 3px; }
.bar-fill.ok { background: var(--primary-color); }
.bar-fill.warning { background: #ff9800; }
.bar-fill.danger { background: var(--error-color, #f44336); }
.inputs-row {
display: grid; grid-template-columns: 1fr 1fr; gap: 8px;
padding-top: 4px; border-top: 1px solid var(--divider-color);
}
.input-field { display: flex; flex-direction: column; gap: 4px; }
.input-field label {
font-size: 11px; color: var(--secondary-text-color);
text-transform: uppercase; letter-spacing: 0.3px;
}
.input-wrap { position: relative; display: flex; align-items: center; }
.input-wrap input {
flex: 1; padding: 6px 32px 6px 8px; font-size: 13px;
background: var(--secondary-background-color, #2c2c2c);
color: var(--primary-text-color);
border: 1px solid var(--divider-color); border-radius: 6px;
font-family: inherit;
}
.input-suffix {
position: absolute; right: 8px;
color: var(--secondary-text-color); font-size: 13px;
pointer-events: none;
}
.actions { display: flex; gap: 8px; align-items: center; }
`],n([y({attribute:!1})],s.prototype,"hass",2),n([l()],s.prototype,"_config",2),n([l()],s.prototype,"_status",2),n([l()],s.prototype,"_busy",2),n([l()],s.prototype,"_error",2),n([l()],s.prototype,"_localMonthly",2),n([l()],s.prototype,"_localYearly",2),n([l()],s.prototype,"_dirty",2);customElements.get("maintenance-budget-section-card")||customElements.define("maintenance-budget-section-card",s);window.customCards=window.customCards||[];window.customCards.push({type:"maintenance-budget-section-card",name:"Maintenance Supporter \u2014 Budget",description:"Inline monthly + yearly budget editor",preview:!1});export{s as MaintenanceBudgetSectionCard};
File diff suppressed because one or more lines are too long
@@ -0,0 +1,2 @@
/*! maintenance_supporter frontend 2.56.0 */
var s=Object.defineProperty;var l=Object.getOwnPropertyDescriptor;var t=(a,r,c,o)=>{for(var e=o>1?void 0:o?l(r,c):r,i=a.length-1,d;i>=0;i--)(d=a[i])&&(e=(o?d(r,c,e):d(e))||e);return o&&e&&s(r,c,e),e};var m={ok:"var(--success-color, #4caf50)",due_soon:"var(--warning-color, #ff9800)",overdue:"var(--error-color, #f44336)",triggered:"var(--deep-orange-color, #ff5722)",archived:"var(--disabled-color, #9e9e9e)",paused:"var(--info-color, #2196f3)"},v={ok:"mdi:check-circle",due_soon:"mdi:alert-circle",overdue:"mdi:alert-octagon",triggered:"mdi:bell-alert",archived:"mdi:archive-outline",paused:"mdi:pause-circle-outline",completed:"mdi:check-circle",skipped:"mdi:skip-next",missed:"mdi:calendar-remove",reset:"mdi:refresh"};export{t as a,m as b,v as c};
@@ -0,0 +1,60 @@
/*! maintenance_supporter frontend 2.56.0 */
import{a as r}from"./chunk-TRW3CRLS.js";var a=r`
ha-card { overflow: hidden; }
.card-content {
padding: 16px;
display: flex; flex-direction: column;
gap: 12px;
}
.header {
display: flex; align-items: center; justify-content: space-between;
gap: 12px;
}
.title {
display: flex; align-items: center; gap: 8px;
font-size: 16px; font-weight: 500;
}
.emoji { font-size: 20px; }
/* Button family — primary action / muted-saved-state / link / icon-with-text */
.btn {
padding: 6px 12px; font-size: 13px;
border-radius: 6px; cursor: pointer;
border: 1px solid var(--divider-color);
background: var(--secondary-background-color, transparent);
color: var(--primary-text-color);
font-weight: 500;
display: inline-flex; align-items: center; gap: 4px;
}
.btn:hover { background: var(--state-icon-color, rgba(255,255,255,0.06)); }
.btn[disabled] { opacity: 0.5; cursor: not-allowed; }
.btn.primary {
background: var(--primary-color);
color: var(--text-primary-color, white);
border-color: var(--primary-color);
}
.btn.primary[disabled] { opacity: 0.6; }
.btn.muted {
background: transparent;
color: var(--secondary-text-color);
border-style: dashed;
}
.btn.muted[disabled] { opacity: 1; cursor: default; }
.btn.muted ha-icon, .btn.primary ha-icon { --mdc-icon-size: 14px; }
.btn.link {
background: transparent; border: none; padding: 6px 4px;
color: var(--primary-color); margin-left: auto;
}
.btn.link:hover { background: transparent; text-decoration: underline; }
/* Error + loading states */
.error {
padding: 8px; border-radius: 6px;
background: rgba(211, 47, 47, 0.1);
color: var(--error-color, #d32f2f); font-size: 13px;
}
.loading {
padding: 24px; text-align: center;
color: var(--secondary-text-color);
}
`;export{a};
File diff suppressed because one or more lines are too long
@@ -0,0 +1,137 @@
/*! maintenance_supporter frontend 2.56.0 */
import{a as m}from"./chunk-XWMJNIQI.js";import{a as u,b as s,d as l,e as h,f as g,g as o,i,k as _,t as p}from"./chunk-TRW3CRLS.js";import{a}from"./chunk-UHANDS57.js";var e=class extends h{constructor(){super(...arguments);this._config={type:""};this._groups={};this._loaded=!1;this._busy=!1;this._error="";this._newName="";this._editingId=null;this._editingName="";this._hasInitiallyLoaded=!1}setConfig(t){this._config=t}getCardSize(){return 2}get _lang(){return this.hass?.language||"en"}get _isAdmin(){return this.hass?.user?.is_admin??!0}updated(t){super.updated(t),t.has("hass")&&this.hass&&!this._hasInitiallyLoaded&&(this._hasInitiallyLoaded=!0,this._load(),_(this._lang).then(()=>this.requestUpdate()))}async _load(){try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/groups"});this._groups=t.groups||{},this._loaded=!0}catch(t){this._error=p(t,this._lang)}}async _addGroup(){if(!this._isAdmin)return;let t=this._newName.trim();if(t){this._busy=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/group/create",name:t}),this._newName="",await this._load()}catch(r){this._error=p(r,this._lang)}finally{this._busy=!1}}}_startEdit(t){this._editingId=t,this._editingName=this._groups[t]?.name||""}async _saveEdit(){if(!this._isAdmin||!this._editingId)return;let t=this._editingName.trim();if(t){this._busy=!0,this._error="";try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/group/update",group_id:this._editingId,name:t}),this._editingId=null,this._editingName="",await this._load()}catch(r){this._error=p(r,this._lang)}finally{this._busy=!1}}}async _deleteGroup(t,r){if(!this._isAdmin)return;let n=(i("group_delete_confirm",this._lang)||'Delete group "{name}"?').replace("{name}",r);if(window.confirm(n)){this._busy=!0;try{await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/group/delete",group_id:t}),await this._load()}catch(d){this._error=p(d,this._lang)}finally{this._busy=!1}}}_onDeepLink(){history.pushState(null,"","/maintenance-supporter?ms_action=open_groups"),window.dispatchEvent(new CustomEvent("location-changed"))}_onKeyDown(t,r){t.key==="Enter"?(t.preventDefault(),r()):t.key==="Escape"&&(t.preventDefault(),this._editingId=null,this._editingName="")}render(){let t=this._lang;if(!this._loaded)return s`<ha-card><div class="loading">${i("loading",t)||"Loading\u2026"}</div></ha-card>`;let r=Object.keys(this._groups);return s`
<ha-card>
<div class="card-content">
<div class="header">
<div class="title">
<span class="emoji">🏷</span>
<span>${this._config.title||i("groups",t)||"Groups"}</span>
<span class="count">${r.length}</span>
</div>
</div>
${this._error?s`<div class="error">${this._error}</div>`:l}
${r.length===0?s`<div class="empty">${i("groups_empty",t)||"No groups yet."}</div>`:s`
<div class="group-list">
${r.map(n=>{let d=this._groups[n],v=d.task_refs?.length??0,b=this._editingId===n;return s`
<div class="group-row">
${b?s`
<input class="edit-input" type="text"
.value=${this._editingName}
?disabled=${this._busy}
@input=${c=>{this._editingName=c.target.value}}
@keydown=${c=>this._onKeyDown(c,this._saveEdit.bind(this))} />
<button class="btn small primary"
@click=${this._saveEdit}
?disabled=${this._busy||!this._editingName.trim()}>
${i("save",t)||"Save"}
</button>
<button class="btn small"
@click=${()=>{this._editingId=null}}>
${i("cancel",t)||"Cancel"}
</button>
`:s`
<span class="group-name">${d.name||"Unnamed"}</span>
<span class="task-count">${v}</span>
${this._isAdmin?s`
<button class="icon-btn"
title="${i("edit",t)||"Edit"}"
@click=${()=>this._startEdit(n)}
?disabled=${this._busy}>
<ha-icon icon="mdi:pencil"></ha-icon>
</button>
<button class="icon-btn danger"
title="${i("delete",t)||"Delete"}"
@click=${()=>this._deleteGroup(n,d.name||"Unnamed")}
?disabled=${this._busy}>
<ha-icon icon="mdi:delete"></ha-icon>
</button>
`:l}
`}
</div>
`})}
</div>
`}
${this._isAdmin?s`
<div class="add-row">
<input type="text"
placeholder="${i("group_new_placeholder",t)||"Add group\u2026"}"
.value=${this._newName}
?disabled=${this._busy}
@input=${n=>{this._newName=n.target.value}}
@keydown=${n=>this._onKeyDown(n,this._addGroup.bind(this))} />
<button class="btn primary"
@click=${this._addGroup}
?disabled=${this._busy||!this._newName.trim()}>
<ha-icon icon="mdi:plus"></ha-icon>
${i("add",t)||"Add"}
</button>
</div>
<button class="btn link" @click=${this._onDeepLink}>
${i("groups_manage_tasks",t)||"Manage task assignments\u2026"}
</button>
`:s`
<button class="btn link" @click=${this._onDeepLink}>
${i("groups_open_panel",t)||"Open in panel"}
</button>
`}
</div>
</ha-card>
`}};e.styles=[m,u`
.count {
font-size: 12px; color: var(--secondary-text-color);
background: var(--secondary-background-color);
padding: 2px 8px; border-radius: 999px;
}
.empty {
padding: 16px; text-align: center;
color: var(--secondary-text-color); font-style: italic;
}
.group-list { display: flex; flex-direction: column; gap: 4px; }
.group-row {
display: flex; align-items: center; gap: 8px;
padding: 6px 8px; border-radius: 6px;
background: var(--secondary-background-color, rgba(255,255,255,0.03));
}
.group-name { flex: 1; font-size: 14px; }
.task-count {
font-size: 11px; color: var(--secondary-text-color);
background: var(--card-background-color, rgba(0,0,0,0.2));
padding: 1px 8px; border-radius: 999px;
font-weight: 500;
}
.edit-input {
flex: 1; padding: 4px 8px; font-size: 14px;
background: var(--card-background-color, #1c1c1c);
color: var(--primary-text-color);
border: 1px solid var(--primary-color); border-radius: 4px;
font-family: inherit;
}
.icon-btn {
background: transparent; border: none; cursor: pointer;
color: var(--secondary-text-color); padding: 4px;
border-radius: 4px;
}
.icon-btn:hover {
background: var(--state-icon-color, rgba(255,255,255,0.06));
color: var(--primary-text-color);
}
.icon-btn.danger:hover { color: var(--error-color); }
.icon-btn ha-icon { --mdc-icon-size: 18px; }
.add-row {
display: flex; gap: 6px;
padding-top: 8px; border-top: 1px solid var(--divider-color);
}
.add-row input {
flex: 1; padding: 6px 8px; font-size: 13px;
background: var(--secondary-background-color, #2c2c2c);
color: var(--primary-text-color);
border: 1px solid var(--divider-color); border-radius: 6px;
font-family: inherit;
}
/* Card-specific overrides on the shared .btn */
.btn.small { padding: 4px 8px; font-size: 12px; }
.btn ha-icon { --mdc-icon-size: 16px; }
`],a([g({attribute:!1})],e.prototype,"hass",2),a([o()],e.prototype,"_config",2),a([o()],e.prototype,"_groups",2),a([o()],e.prototype,"_loaded",2),a([o()],e.prototype,"_busy",2),a([o()],e.prototype,"_error",2),a([o()],e.prototype,"_newName",2),a([o()],e.prototype,"_editingId",2),a([o()],e.prototype,"_editingName",2);customElements.get("maintenance-groups-section-card")||customElements.define("maintenance-groups-section-card",e);window.customCards=window.customCards||[];window.customCards.push({type:"maintenance-groups-section-card",name:"Maintenance Supporter \u2014 Groups",description:"Inline group CRUD",preview:!1});export{e as MaintenanceGroupsSectionCard};
@@ -0,0 +1,122 @@
/*! maintenance_supporter frontend 2.56.0 */
import{a as m}from"./chunk-XWMJNIQI.js";import{a as h,b as n,d as c,e as _,f as v,g as r,i as e,k as f,t as l}from"./chunk-TRW3CRLS.js";import{a as i}from"./chunk-UHANDS57.js";var a=class extends _{constructor(){super(...arguments);this._config={type:""};this._state=null;this._busy=!1;this._error="";this._localStart="";this._localEnd="";this._localBuffer=7;this._dirty=!1;this._loaded=!1}setConfig(t){this._config=t}getCardSize(){return 2}get _lang(){return this.hass?.language||"en"}get _isAdmin(){return this.hass?.user?.is_admin??!0}updated(t){super.updated(t),t.has("hass")&&this.hass&&!this._loaded&&(this._loaded=!0,this._load(),f(this._lang).then(()=>this.requestUpdate()))}async _load(){try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/vacation/state"});this._state=t,this._localStart=t.start||"",this._localEnd=t.end||"",this._localBuffer=t.buffer_days??7,this._dirty=!1}catch(t){this._error=l(t,this._lang)}}async _toggleEnabled(t){this._busy=!0,this._error="";try{let s=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/vacation/update",enabled:t});this._state=s}catch(s){this._error=l(s,this._lang)}finally{this._busy=!1}}async _save(){if(this._isAdmin){this._busy=!0,this._error="";try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/vacation/update",start:this._localStart||null,end:this._localEnd||null,buffer_days:this._localBuffer});this._state=t,this._dirty=!1}catch(t){this._error=l(t,this._lang)}finally{this._busy=!1}}}async _endNow(){if(this._isAdmin&&window.confirm(e("vacation_end_now_confirm",this._lang)||"End vacation immediately?")){this._busy=!0;try{let t=await this.hass.connection.sendMessagePromise({type:"maintenance_supporter/vacation/end_now"});this._state=t,this._localStart=t.start||"",this._localEnd=t.end||""}catch(t){this._error=l(t,this._lang)}finally{this._busy=!1}}}_onDeepLink(){history.pushState(null,"","/maintenance-supporter?ms_action=open_vacation"),window.dispatchEvent(new CustomEvent("location-changed"))}render(){let t=this._lang,s=this._state;if(!s)return n`<ha-card><div class="loading">${e("loading",t)||"Loading\u2026"}</div></ha-card>`;let p=s.is_active===!0,d=s.enabled===!0,u=s.exempt_task_ids?.length??0,b=p?e("vacation_status_active",t)||"Active now":d?e("vacation_status_scheduled",t)||"Scheduled":e("vacation_status_inactive",t)||"Inactive",g=p?"active":d?"scheduled":"inactive";return n`
<ha-card>
<div class="card-content">
<div class="header">
<div class="title">
<span class="emoji">🏖</span>
<span>${this._config.title||e("vacation_mode",t)||"Vacation mode"}</span>
</div>
<span class="status-pill ${g}">${b}</span>
</div>
${this._error?n`<div class="error">${this._error}</div>`:c}
${this._isAdmin?n`
<div class="row toggle-row">
<label>${e("enable",t)||"Enable"}</label>
<ha-switch
.checked=${d}
.disabled=${this._busy}
@change=${o=>this._toggleEnabled(o.target.checked)}
></ha-switch>
</div>
<div class="dates-row">
<div class="date-field">
<label>${e("vacation_start",t)||"Start"}</label>
<input type="date" .value=${this._localStart}
?disabled=${this._busy}
@input=${o=>{this._localStart=o.target.value,this._dirty=!0}} />
</div>
<div class="date-field">
<label>${e("vacation_end",t)||"End"}</label>
<input type="date" .value=${this._localEnd}
?disabled=${this._busy}
@input=${o=>{this._localEnd=o.target.value,this._dirty=!0}} />
</div>
<div class="date-field buffer">
<label>${e("vacation_buffer",t)||"Buffer days"}</label>
<input type="number" min="0" max="14"
.value=${String(this._localBuffer)}
?disabled=${this._busy}
@input=${o=>{this._localBuffer=parseInt(o.target.value,10)||0,this._dirty=!0}} />
</div>
</div>
<div class="actions">
<button class="btn ${this._dirty?"primary":"muted"}"
@click=${this._save}
?disabled=${this._busy||!this._dirty}>
<ha-icon icon="${this._dirty?"mdi:content-save":"mdi:check"}"></ha-icon>
${this._dirty?e("save",t)||"Save":e("saved",t)||"Saved"}
</button>
${p?n`<button class="btn"
@click=${this._endNow}
?disabled=${this._busy}>
${e("vacation_end_now",t)||"End now"}
</button>`:c}
${u>0?n`<button class="btn link"
@click=${this._onDeepLink}>
${u} ${e("vacation_exempt_count",t)||"exempt"}
</button>`:n`<button class="btn link"
@click=${this._onDeepLink}>
${e("vacation_advanced",t)||"Advanced\u2026"}
</button>`}
</div>
`:n`
<div class="readonly">
${d&&s.start&&s.end?n`<div>${s.start}${s.end}</div>`:c}
<button class="btn link" @click=${this._onDeepLink}>
${e("vacation_open_panel",t)||"Open in panel"}
</button>
</div>
`}
</div>
</ha-card>
`}};a.styles=[m,h`
.status-pill {
font-size: 11px; font-weight: 600;
padding: 3px 8px; border-radius: 999px;
text-transform: uppercase; letter-spacing: 0.5px;
}
.status-pill.active {
background: rgba(76, 175, 80, 0.15);
color: #4caf50;
}
.status-pill.scheduled {
background: rgba(255, 152, 0, 0.15);
color: #ff9800;
}
.status-pill.inactive {
background: rgba(158, 158, 158, 0.15);
color: var(--secondary-text-color);
}
.row.toggle-row {
display: flex; align-items: center; justify-content: space-between;
}
.row.toggle-row label {
font-size: 14px; color: var(--primary-text-color);
}
.dates-row {
display: grid; grid-template-columns: 1fr 1fr 100px; gap: 10px;
}
.date-field.buffer label { white-space: nowrap; }
.date-field { display: flex; flex-direction: column; gap: 4px; }
.date-field label {
font-size: 11px; color: var(--secondary-text-color);
text-transform: uppercase; letter-spacing: 0.3px;
}
.date-field input {
padding: 6px 8px; font-size: 13px;
background: var(--secondary-background-color, #2c2c2c);
color: var(--primary-text-color);
border: 1px solid var(--divider-color); border-radius: 6px;
font-family: inherit;
}
.date-field input:disabled { opacity: 0.5; cursor: not-allowed; }
.actions {
display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
}
.readonly { display: flex; flex-direction: column; gap: 8px; }
`],i([v({attribute:!1})],a.prototype,"hass",2),i([r()],a.prototype,"_config",2),i([r()],a.prototype,"_state",2),i([r()],a.prototype,"_busy",2),i([r()],a.prototype,"_error",2),i([r()],a.prototype,"_localStart",2),i([r()],a.prototype,"_localEnd",2),i([r()],a.prototype,"_localBuffer",2),i([r()],a.prototype,"_dirty",2);customElements.get("maintenance-vacation-section-card")||customElements.define("maintenance-vacation-section-card",a);window.customCards=window.customCards||[];window.customCards.push({type:"maintenance-vacation-section-card",name:"Maintenance Supporter \u2014 Vacation",description:"Inline vacation mode toggle + dates",preview:!1});export{a as MaintenanceVacationSectionCard};
File diff suppressed because one or more lines are too long
@@ -22,5 +22,5 @@
"requirements": [
"pypdf>=4.3.0"
],
"version": "2.55.0"
"version": "2.56.0"
}
@@ -39,6 +39,12 @@ complete:
number:
mode: box
step: any
completed_by:
name: Completed by
description: Person who performed the maintenance (must be linked to a Home Assistant user). When omitted, the user triggering the call is recorded.
selector:
entity:
domain: person
reset:
name: Reset Maintenance
@@ -310,6 +316,17 @@ update_task:
description: List of tags for filtering and reporting.
selector:
object:
responsible_user:
name: Responsible user
description: Assign the task to this person (must be linked to a Home Assistant user).
selector:
entity:
domain: person
clear_responsible_user:
name: Clear responsible user
description: Remove the current assignment. Cannot be combined with "Responsible user".
selector:
boolean:
delete_task:
name: Delete Task
@@ -230,7 +230,8 @@
"interval_days": "Safety interval",
"interval_unit": "Interval unit",
"warning_days": "Warning days before due",
"go_back": "Go back"
"go_back": "Go back",
"auto_complete_on_recovery": "Auto-complete when the sensor recovers"
},
"data_description": {
"trigger_above": "Trigger maintenance when value exceeds this threshold.",
@@ -251,7 +252,9 @@
"interval_days": "Safety interval",
"interval_unit": "Interval unit",
"warning_days": "Warning days before due",
"go_back": "Go back"
"go_back": "Go back",
"auto_complete_on_recovery": "Auto-complete when the sensor recovers",
"trigger_baseline_value": "Start reading (optional)"
},
"data_description": {
"trigger_target_value": "Trigger maintenance when this accumulated value is reached.",
@@ -272,7 +275,8 @@
"interval_days": "Safety interval",
"interval_unit": "Interval unit",
"warning_days": "Warning days before due",
"go_back": "Go back"
"go_back": "Go back",
"auto_complete_on_recovery": "Auto-complete when the sensor recovers"
},
"data_description": {
"trigger_from_state": "Only count transitions from this state. Leave empty for any.",
@@ -293,7 +297,8 @@
"interval_days": "Safety interval",
"interval_unit": "Interval unit",
"warning_days": "Warning days before due",
"go_back": "Go back"
"go_back": "Go back",
"auto_complete_on_recovery": "Auto-complete when the sensor recovers"
},
"data_description": {
"trigger_runtime_hours": "Trigger maintenance after this many hours of accumulated runtime.",
@@ -309,7 +314,8 @@
"description": "Choose how multiple conditions should be combined.",
"data": {
"compound_logic": "Logic",
"go_back": "Go back"
"go_back": "Go back",
"auto_complete_on_recovery": "Auto-complete when the sensor recovers"
},
"data_description": {
"compound_logic": "AND = all conditions must trigger. OR = any condition triggers.",
@@ -882,7 +888,8 @@
"interval_days": "Safety interval",
"interval_unit": "Interval unit",
"warning_days": "Warning days before due",
"go_back": "Go back"
"go_back": "Go back",
"auto_complete_on_recovery": "Auto-complete when the sensor recovers"
},
"data_description": {
"trigger_above": "Trigger maintenance when value exceeds this threshold.",
@@ -902,7 +909,9 @@
"interval_days": "Safety interval",
"interval_unit": "Interval unit",
"warning_days": "Warning days before due",
"go_back": "Go back"
"go_back": "Go back",
"auto_complete_on_recovery": "Auto-complete when the sensor recovers",
"trigger_baseline_value": "Start reading (optional)"
},
"data_description": {
"trigger_target_value": "Trigger maintenance when this accumulated value is reached.",
@@ -922,7 +931,8 @@
"interval_days": "Safety interval",
"interval_unit": "Interval unit",
"warning_days": "Warning days before due",
"go_back": "Go back"
"go_back": "Go back",
"auto_complete_on_recovery": "Auto-complete when the sensor recovers"
},
"data_description": {
"trigger_from_state": "Only count transitions from this state. Leave empty for any.",
@@ -942,7 +952,8 @@
"interval_days": "Safety interval",
"interval_unit": "Interval unit",
"warning_days": "Warning days before due",
"go_back": "Go back"
"go_back": "Go back",
"auto_complete_on_recovery": "Auto-complete when the sensor recovers"
},
"data_description": {
"trigger_runtime_hours": "Trigger maintenance after this many hours of accumulated runtime.",
@@ -957,7 +968,8 @@
"description": "Choose how multiple conditions should be combined.",
"data": {
"compound_logic": "Logic",
"go_back": "Go back"
"go_back": "Go back",
"auto_complete_on_recovery": "Auto-complete when the sensor recovers"
},
"data_description": {
"compound_logic": "AND = all conditions must trigger. OR = any condition triggers."
@@ -1440,6 +1452,10 @@
"reading_value": {
"name": "Reading value",
"description": "Recorded value for reading-type tasks (meter reading, level check)."
},
"completed_by": {
"name": "Completed by",
"description": "Person who performed the maintenance (must be linked to a Home Assistant user). When omitted, the user triggering the call is recorded."
}
}
},
@@ -1629,6 +1645,14 @@
"labels": {
"name": "Labels",
"description": "List of tags for filtering and reporting."
},
"responsible_user": {
"name": "Responsible user",
"description": "Assign the task to this person (must be linked to a Home Assistant user)."
},
"clear_responsible_user": {
"name": "Clear responsible user",
"description": "Remove the current assignment. Cannot be combined with \"Responsible user\"."
}
}
},
@@ -230,7 +230,8 @@
"interval_days": "Bezpečnostní interval",
"interval_unit": "Jednotka intervalu",
"warning_days": "Dny upozornění před termínem",
"go_back": "Zpět"
"go_back": "Zpět",
"auto_complete_on_recovery": "Automaticky dokončit při zotavení senzoru"
},
"data_description": {
"trigger_above": "Spustit údržbu, když hodnota překročí tento práh.",
@@ -251,7 +252,9 @@
"interval_days": "Bezpečnostní interval",
"interval_unit": "Jednotka intervalu",
"warning_days": "Dny upozornění před termínem",
"go_back": "Zpět"
"go_back": "Zpět",
"auto_complete_on_recovery": "Automaticky dokončit při zotavení senzoru",
"trigger_baseline_value": "Počáteční hodnota (volitelné)"
},
"data_description": {
"trigger_target_value": "Spustit údržbu, když je dosaženo této kumulované hodnoty.",
@@ -272,7 +275,8 @@
"interval_days": "Bezpečnostní interval",
"interval_unit": "Jednotka intervalu",
"warning_days": "Dny upozornění před termínem",
"go_back": "Zpět"
"go_back": "Zpět",
"auto_complete_on_recovery": "Automaticky dokončit při zotavení senzoru"
},
"data_description": {
"trigger_from_state": "Počítat pouze přechody z tohoto stavu. Nechte prázdné pro libovolný.",
@@ -293,7 +297,8 @@
"interval_days": "Bezpečnostní interval",
"interval_unit": "Jednotka intervalu",
"warning_days": "Dny upozornění před termínem",
"go_back": "Zpět"
"go_back": "Zpět",
"auto_complete_on_recovery": "Automaticky dokončit při zotavení senzoru"
},
"data_description": {
"trigger_runtime_hours": "Spustit údržbu po tolika hodinách kumulované doby běhu.",
@@ -309,7 +314,8 @@
"description": "Vyberte, jak mají být kombinovány více podmínek.",
"data": {
"compound_logic": "Logika",
"go_back": "Zpět"
"go_back": "Zpět",
"auto_complete_on_recovery": "Automaticky dokončit při zotavení senzoru"
},
"data_description": {
"compound_logic": "AND = všechny podmínky musí být splněny. OR = stačí jedna podmínka.",
@@ -720,7 +726,8 @@
"description": "Vyberte, jak mají být kombinovány více podmínek.",
"data": {
"compound_logic": "Logika",
"go_back": "Zpět"
"go_back": "Zpět",
"auto_complete_on_recovery": "Automaticky dokončit při zotavení senzoru"
},
"data_description": {
"compound_logic": "AND = všechny podmínky musí být splněny. OR = stačí jedna podmínka."
@@ -999,7 +1006,8 @@
"interval_days": "Bezpečnostní interval",
"interval_unit": "Jednotka intervalu",
"warning_days": "Dny upozornění před termínem",
"go_back": "Zpět"
"go_back": "Zpět",
"auto_complete_on_recovery": "Automaticky dokončit při zotavení senzoru"
},
"data_description": {
"trigger_above": "Spustit údržbu, když hodnota překročí tento práh.",
@@ -1019,7 +1027,9 @@
"interval_days": "Bezpečnostní interval",
"interval_unit": "Jednotka intervalu",
"warning_days": "Dny upozornění před termínem",
"go_back": "Zpět"
"go_back": "Zpět",
"auto_complete_on_recovery": "Automaticky dokončit při zotavení senzoru",
"trigger_baseline_value": "Počáteční hodnota (volitelné)"
},
"data_description": {
"trigger_target_value": "Spustit údržbu, když je dosaženo této kumulované hodnoty.",
@@ -1039,7 +1049,8 @@
"interval_days": "Bezpečnostní interval",
"interval_unit": "Jednotka intervalu",
"warning_days": "Dny upozornění před termínem",
"go_back": "Zpět"
"go_back": "Zpět",
"auto_complete_on_recovery": "Automaticky dokončit při zotavení senzoru"
},
"data_description": {
"trigger_from_state": "Počítat pouze přechody z tohoto stavu. Nechte prázdné pro libovolný.",
@@ -1059,7 +1070,8 @@
"interval_days": "Bezpečnostní interval",
"interval_unit": "Jednotka intervalu",
"warning_days": "Dny upozornění před termínem",
"go_back": "Zpět"
"go_back": "Zpět",
"auto_complete_on_recovery": "Automaticky dokončit při zotavení senzoru"
},
"data_description": {
"trigger_runtime_hours": "Spustit údržbu po tolika hodinách kumulované doby běhu.",
@@ -1500,6 +1512,10 @@
"reading_value": {
"name": "Hodnota odečtu",
"description": "Zaznamenaná hodnota pro odečtové úkoly (odečet měřiče, kontrola hladiny)."
},
"completed_by": {
"name": "Dokončil(a)",
"description": "Osoba, která údržbu provedla (musí být propojena s uživatelem Home Assistant). Pokud není uvedeno, zaznamená se volající uživatel."
}
}
},
@@ -1689,6 +1705,14 @@
"labels": {
"name": "Štítky",
"description": "Seznam štítků pro filtrování a přehledy."
},
"responsible_user": {
"name": "Odpovědný uživatel",
"description": "Přiřadí úkol této osobě (musí být propojena s uživatelem Home Assistant)."
},
"clear_responsible_user": {
"name": "Zrušit přiřazení",
"description": "Zruší aktuální přiřazení. Nelze kombinovat s „Odpovědný uživatel“."
}
}
},
@@ -230,7 +230,8 @@
"interval_days": "Sikkerhedsinterval",
"interval_unit": "Intervalenhed",
"warning_days": "Advarsel i dage før forfald",
"go_back": "Gå tilbage"
"go_back": "Gå tilbage",
"auto_complete_on_recovery": "Fuldfør automatisk når sensoren retter sig"
},
"data_description": {
"trigger_above": "Udløs vedligeholdelse, når værdien overskrider denne tærskel.",
@@ -251,7 +252,9 @@
"interval_days": "Sikkerhedsinterval",
"interval_unit": "Intervalenhed",
"warning_days": "Advarsel i dage før forfald",
"go_back": "Gå tilbage"
"go_back": "Gå tilbage",
"auto_complete_on_recovery": "Fuldfør automatisk når sensoren retter sig",
"trigger_baseline_value": "Startværdi (valgfrit)"
},
"data_description": {
"trigger_target_value": "Udløs vedligeholdelse, når denne akkumulerede værdi er nået.",
@@ -272,7 +275,8 @@
"interval_days": "Sikkerhedsinterval",
"interval_unit": "Intervalenhed",
"warning_days": "Advarsel i dage før forfald",
"go_back": "Gå tilbage"
"go_back": "Gå tilbage",
"auto_complete_on_recovery": "Fuldfør automatisk når sensoren retter sig"
},
"data_description": {
"trigger_from_state": "Tæl kun overgange fra denne tilstand. Lad feltet stå tomt for alle.",
@@ -293,7 +297,8 @@
"interval_days": "Sikkerhedsinterval",
"interval_unit": "Intervalenhed",
"warning_days": "Advarsel i dage før forfald",
"go_back": "Gå tilbage"
"go_back": "Gå tilbage",
"auto_complete_on_recovery": "Fuldfør automatisk når sensoren retter sig"
},
"data_description": {
"trigger_runtime_hours": "Udløs vedligeholdelse efter så mange timers akkumuleret driftstid.",
@@ -309,7 +314,8 @@
"description": "Vælg hvordan flere betingelser skal kombineres.",
"data": {
"compound_logic": "Logik",
"go_back": "Gå tilbage"
"go_back": "Gå tilbage",
"auto_complete_on_recovery": "Fuldfør automatisk når sensoren retter sig"
},
"data_description": {
"compound_logic": "OG = alle betingelser skal udløses. ELLER = enhver betingelse udløser.",
@@ -720,7 +726,8 @@
"description": "Vælg hvordan flere betingelser skal kombineres.",
"data": {
"compound_logic": "Logik",
"go_back": "Gå tilbage"
"go_back": "Gå tilbage",
"auto_complete_on_recovery": "Fuldfør automatisk når sensoren retter sig"
},
"data_description": {
"compound_logic": "OG = alle betingelser skal udløses. ELLER = enhver betingelse udløser."
@@ -999,7 +1006,8 @@
"interval_days": "Sikkerhedsinterval",
"interval_unit": "Intervalenhed",
"warning_days": "Advarsel i dage før forfald",
"go_back": "Gå tilbage"
"go_back": "Gå tilbage",
"auto_complete_on_recovery": "Fuldfør automatisk når sensoren retter sig"
},
"data_description": {
"trigger_above": "Udløs vedligeholdelse, når værdien overskrider denne tærskel.",
@@ -1019,7 +1027,9 @@
"interval_days": "Sikkerhedsinterval",
"interval_unit": "Intervalenhed",
"warning_days": "Advarsel i dage før forfald",
"go_back": "Gå tilbage"
"go_back": "Gå tilbage",
"auto_complete_on_recovery": "Fuldfør automatisk når sensoren retter sig",
"trigger_baseline_value": "Startværdi (valgfrit)"
},
"data_description": {
"trigger_target_value": "Udløs vedligeholdelse, når denne akkumulerede værdi er nået.",
@@ -1039,7 +1049,8 @@
"interval_days": "Sikkerhedsinterval",
"interval_unit": "Intervalenhed",
"warning_days": "Advarsel i dage før forfald",
"go_back": "Gå tilbage"
"go_back": "Gå tilbage",
"auto_complete_on_recovery": "Fuldfør automatisk når sensoren retter sig"
},
"data_description": {
"trigger_from_state": "Tæl kun overgange fra denne tilstand. Lad feltet stå tomt for alle.",
@@ -1059,7 +1070,8 @@
"interval_days": "Sikkerhedsinterval",
"interval_unit": "Intervalenhed",
"warning_days": "Advarsel i dage før forfald",
"go_back": "Gå tilbage"
"go_back": "Gå tilbage",
"auto_complete_on_recovery": "Fuldfør automatisk når sensoren retter sig"
},
"data_description": {
"trigger_runtime_hours": "Udløs vedligeholdelse efter så mange timers akkumuleret driftstid.",
@@ -1500,6 +1512,10 @@
"reading_value": {
"name": "Aflæst værdi",
"description": "Registreret værdi for aflæsningsopgaver (måleraflæsning, niveaukontrol)."
},
"completed_by": {
"name": "Udført af",
"description": "Person der udførte vedligeholdelsen (skal være knyttet til en Home Assistant-bruger). Hvis udeladt registreres brugeren der udløste kaldet."
}
}
},
@@ -1689,6 +1705,14 @@
"labels": {
"name": "Etiketter",
"description": "Liste over etiketter til filtrering og rapportering."
},
"responsible_user": {
"name": "Ansvarlig bruger",
"description": "Tildeler opgaven til denne person (skal være knyttet til en Home Assistant-bruger)."
},
"clear_responsible_user": {
"name": "Fjern ansvarlig",
"description": "Fjerner den aktuelle tildeling. Kan ikke kombineres med \"Ansvarlig bruger\"."
}
}
},
@@ -230,7 +230,8 @@
"interval_days": "Sicherheitsintervall",
"interval_unit": "Intervalleinheit",
"warning_days": "Warntage vor Fälligkeit",
"go_back": "Zurück"
"go_back": "Zurück",
"auto_complete_on_recovery": "Bei Sensor-Erholung automatisch abschließen"
},
"data_description": {
"trigger_above": "Wartung auslösen, wenn der Wert diesen Schwellwert überschreitet.",
@@ -251,7 +252,9 @@
"interval_days": "Sicherheitsintervall",
"interval_unit": "Intervalleinheit",
"warning_days": "Warntage vor Fälligkeit",
"go_back": "Zurück"
"go_back": "Zurück",
"auto_complete_on_recovery": "Bei Sensor-Erholung automatisch abschließen",
"trigger_baseline_value": "Startwert (optional)"
},
"data_description": {
"trigger_target_value": "Wartung auslösen, wenn dieser akkumulierte Wert erreicht ist.",
@@ -272,7 +275,8 @@
"interval_days": "Sicherheitsintervall",
"interval_unit": "Intervalleinheit",
"warning_days": "Warntage vor Fälligkeit",
"go_back": "Zurück"
"go_back": "Zurück",
"auto_complete_on_recovery": "Bei Sensor-Erholung automatisch abschließen"
},
"data_description": {
"trigger_from_state": "Nur Übergänge von diesem Zustand zählen. Leer lassen für alle.",
@@ -293,7 +297,8 @@
"interval_days": "Sicherheitsintervall",
"interval_unit": "Intervalleinheit",
"warning_days": "Warntage vor Fälligkeit",
"go_back": "Zurück"
"go_back": "Zurück",
"auto_complete_on_recovery": "Bei Sensor-Erholung automatisch abschließen"
},
"data_description": {
"trigger_runtime_hours": "Wartung auslösen nach dieser Anzahl akkumulierter Betriebsstunden.",
@@ -309,7 +314,8 @@
"description": "Wählen Sie, wie mehrere Bedingungen kombiniert werden sollen.",
"data": {
"compound_logic": "Logik",
"go_back": "Zurück"
"go_back": "Zurück",
"auto_complete_on_recovery": "Bei Sensor-Erholung automatisch abschließen"
},
"data_description": {
"compound_logic": "UND = alle Bedingungen müssen auslösen. ODER = eine Bedingung genügt.",
@@ -906,7 +912,8 @@
"interval_days": "Sicherheitsintervall",
"interval_unit": "Intervalleinheit",
"warning_days": "Warntage vor Fälligkeit",
"go_back": "Zurück"
"go_back": "Zurück",
"auto_complete_on_recovery": "Bei Sensor-Erholung automatisch abschließen"
},
"data_description": {
"trigger_above": "Wartung auslösen, wenn der Wert diesen Schwellenwert überschreitet.",
@@ -926,7 +933,9 @@
"interval_days": "Sicherheitsintervall",
"interval_unit": "Intervalleinheit",
"warning_days": "Warntage vor Fälligkeit",
"go_back": "Zurück"
"go_back": "Zurück",
"auto_complete_on_recovery": "Bei Sensor-Erholung automatisch abschließen",
"trigger_baseline_value": "Startwert (optional)"
},
"data_description": {
"trigger_target_value": "Wartung auslösen, wenn dieser kumulierte Wert erreicht ist.",
@@ -946,7 +955,8 @@
"interval_days": "Sicherheitsintervall",
"interval_unit": "Intervalleinheit",
"warning_days": "Warntage vor Fälligkeit",
"go_back": "Zurück"
"go_back": "Zurück",
"auto_complete_on_recovery": "Bei Sensor-Erholung automatisch abschließen"
},
"data_description": {
"trigger_from_state": "Nur Übergänge von diesem Zustand zählen. Leer lassen für alle.",
@@ -966,7 +976,8 @@
"interval_days": "Sicherheitsintervall",
"interval_unit": "Intervalleinheit",
"warning_days": "Warntage vor Fälligkeit",
"go_back": "Zurück"
"go_back": "Zurück",
"auto_complete_on_recovery": "Bei Sensor-Erholung automatisch abschließen"
},
"data_description": {
"trigger_runtime_hours": "Wartung auslösen nach dieser Anzahl von Stunden kumulierter Laufzeit.",
@@ -981,7 +992,8 @@
"description": "Wählen Sie, wie mehrere Bedingungen kombiniert werden sollen.",
"data": {
"compound_logic": "Logik",
"go_back": "Zurück"
"go_back": "Zurück",
"auto_complete_on_recovery": "Bei Sensor-Erholung automatisch abschließen"
},
"data_description": {
"compound_logic": "UND = alle Bedingungen müssen auslösen. ODER = eine Bedingung genügt."
@@ -1500,6 +1512,10 @@
"reading_value": {
"name": "Ablesewert",
"description": "Erfasster Wert für Ablese-Aufgaben (Zählerstand, Füllstandskontrolle)."
},
"completed_by": {
"name": "Erledigt von",
"description": "Person, die die Wartung durchgeführt hat (muss mit einem Home-Assistant-Benutzer verknüpft sein). Ohne Angabe wird der auslösende Benutzer erfasst."
}
}
},
@@ -1689,6 +1705,14 @@
"labels": {
"name": "Labels",
"description": "Liste von Tags für Filterung und Auswertung."
},
"responsible_user": {
"name": "Verantwortlicher Benutzer",
"description": "Weist die Aufgabe dieser Person zu (muss mit einem Home-Assistant-Benutzer verknüpft sein)."
},
"clear_responsible_user": {
"name": "Zuweisung entfernen",
"description": "Entfernt die aktuelle Zuweisung. Nicht mit „Verantwortlicher Benutzer“ kombinierbar."
}
}
},
@@ -230,7 +230,8 @@
"interval_days": "Safety interval",
"interval_unit": "Interval unit",
"warning_days": "Warning days before due",
"go_back": "Go back"
"go_back": "Go back",
"auto_complete_on_recovery": "Auto-complete when the sensor recovers"
},
"data_description": {
"trigger_above": "Trigger maintenance when value exceeds this threshold.",
@@ -251,7 +252,9 @@
"interval_days": "Safety interval",
"interval_unit": "Interval unit",
"warning_days": "Warning days before due",
"go_back": "Go back"
"go_back": "Go back",
"auto_complete_on_recovery": "Auto-complete when the sensor recovers",
"trigger_baseline_value": "Start reading (optional)"
},
"data_description": {
"trigger_target_value": "Trigger maintenance when this accumulated value is reached.",
@@ -272,7 +275,8 @@
"interval_days": "Safety interval",
"interval_unit": "Interval unit",
"warning_days": "Warning days before due",
"go_back": "Go back"
"go_back": "Go back",
"auto_complete_on_recovery": "Auto-complete when the sensor recovers"
},
"data_description": {
"trigger_from_state": "Only count transitions from this state. Leave empty for any.",
@@ -293,7 +297,8 @@
"interval_days": "Safety interval",
"interval_unit": "Interval unit",
"warning_days": "Warning days before due",
"go_back": "Go back"
"go_back": "Go back",
"auto_complete_on_recovery": "Auto-complete when the sensor recovers"
},
"data_description": {
"trigger_runtime_hours": "Trigger maintenance after this many hours of accumulated runtime.",
@@ -309,7 +314,8 @@
"description": "Choose how multiple conditions should be combined.",
"data": {
"compound_logic": "Logic",
"go_back": "Go back"
"go_back": "Go back",
"auto_complete_on_recovery": "Auto-complete when the sensor recovers"
},
"data_description": {
"compound_logic": "AND = all conditions must trigger. OR = any condition triggers.",
@@ -720,7 +726,8 @@
"description": "Choose how multiple conditions should be combined.",
"data": {
"compound_logic": "Logic",
"go_back": "Go back"
"go_back": "Go back",
"auto_complete_on_recovery": "Auto-complete when the sensor recovers"
},
"data_description": {
"compound_logic": "AND = all conditions must trigger. OR = any condition triggers."
@@ -999,7 +1006,8 @@
"interval_days": "Safety interval",
"interval_unit": "Interval unit",
"warning_days": "Warning days before due",
"go_back": "Go back"
"go_back": "Go back",
"auto_complete_on_recovery": "Auto-complete when the sensor recovers"
},
"data_description": {
"trigger_above": "Trigger maintenance when value exceeds this threshold.",
@@ -1019,7 +1027,9 @@
"interval_days": "Safety interval",
"interval_unit": "Interval unit",
"warning_days": "Warning days before due",
"go_back": "Go back"
"go_back": "Go back",
"auto_complete_on_recovery": "Auto-complete when the sensor recovers",
"trigger_baseline_value": "Start reading (optional)"
},
"data_description": {
"trigger_target_value": "Trigger maintenance when this accumulated value is reached.",
@@ -1039,7 +1049,8 @@
"interval_days": "Safety interval",
"interval_unit": "Interval unit",
"warning_days": "Warning days before due",
"go_back": "Go back"
"go_back": "Go back",
"auto_complete_on_recovery": "Auto-complete when the sensor recovers"
},
"data_description": {
"trigger_from_state": "Only count transitions from this state. Leave empty for any.",
@@ -1059,7 +1070,8 @@
"interval_days": "Safety interval",
"interval_unit": "Interval unit",
"warning_days": "Warning days before due",
"go_back": "Go back"
"go_back": "Go back",
"auto_complete_on_recovery": "Auto-complete when the sensor recovers"
},
"data_description": {
"trigger_runtime_hours": "Trigger maintenance after this many hours of accumulated runtime.",
@@ -1500,6 +1512,10 @@
"reading_value": {
"name": "Reading value",
"description": "Recorded value for reading-type tasks (meter reading, level check)."
},
"completed_by": {
"name": "Completed by",
"description": "Person who performed the maintenance (must be linked to a Home Assistant user). When omitted, the user triggering the call is recorded."
}
}
},
@@ -1689,6 +1705,14 @@
"labels": {
"name": "Labels",
"description": "List of tags for filtering and reporting."
},
"responsible_user": {
"name": "Responsible user",
"description": "Assign the task to this person (must be linked to a Home Assistant user)."
},
"clear_responsible_user": {
"name": "Clear responsible user",
"description": "Remove the current assignment. Cannot be combined with \"Responsible user\"."
}
}
},
@@ -230,7 +230,8 @@
"interval_days": "Intervalo de seguridad",
"interval_unit": "Unidad de intervalo",
"warning_days": "Días de aviso antes del vencimiento",
"go_back": "Volver"
"go_back": "Volver",
"auto_complete_on_recovery": "Completar automáticamente cuando el sensor se recupere"
},
"data_description": {
"trigger_above": "Activar mantenimiento cuando el valor supere este umbral.",
@@ -251,7 +252,9 @@
"interval_days": "Intervalo de seguridad",
"interval_unit": "Unidad de intervalo",
"warning_days": "Días de aviso antes del vencimiento",
"go_back": "Volver"
"go_back": "Volver",
"auto_complete_on_recovery": "Completar automáticamente cuando el sensor se recupere",
"trigger_baseline_value": "Valor inicial (opcional)"
},
"data_description": {
"trigger_target_value": "Activar mantenimiento cuando se alcance este valor acumulado.",
@@ -272,7 +275,8 @@
"interval_days": "Intervalo de seguridad",
"interval_unit": "Unidad de intervalo",
"warning_days": "Días de aviso antes del vencimiento",
"go_back": "Volver"
"go_back": "Volver",
"auto_complete_on_recovery": "Completar automáticamente cuando el sensor se recupere"
},
"data_description": {
"trigger_from_state": "Contar solo transiciones desde este estado. Dejar vacío para todos.",
@@ -293,7 +297,8 @@
"interval_days": "Intervalo de seguridad",
"interval_unit": "Unidad de intervalo",
"warning_days": "Días de aviso antes del vencimiento",
"go_back": "Volver"
"go_back": "Volver",
"auto_complete_on_recovery": "Completar automáticamente cuando el sensor se recupere"
},
"data_description": {
"trigger_runtime_hours": "Activar mantenimiento después de este número de horas de funcionamiento acumulado.",
@@ -309,7 +314,8 @@
"description": "Elija cómo se deben combinar múltiples condiciones.",
"data": {
"compound_logic": "Lógica",
"go_back": "Volver"
"go_back": "Volver",
"auto_complete_on_recovery": "Completar automáticamente cuando el sensor se recupere"
},
"data_description": {
"compound_logic": "Y = todas las condiciones deben activarse. O = cualquier condición activa.",
@@ -720,7 +726,8 @@
"description": "Elija cómo se deben combinar múltiples condiciones.",
"data": {
"compound_logic": "Lógica",
"go_back": "Volver"
"go_back": "Volver",
"auto_complete_on_recovery": "Completar automáticamente cuando el sensor se recupere"
},
"data_description": {
"compound_logic": "Y = todas las condiciones deben activarse. O = cualquier condición activa."
@@ -999,7 +1006,8 @@
"interval_days": "Intervalo de seguridad",
"interval_unit": "Unidad de intervalo",
"warning_days": "Días de aviso antes del vencimiento",
"go_back": "Volver"
"go_back": "Volver",
"auto_complete_on_recovery": "Completar automáticamente cuando el sensor se recupere"
},
"data_description": {
"trigger_above": "Activar mantenimiento cuando el valor exceda este umbral.",
@@ -1019,7 +1027,9 @@
"interval_days": "Intervalo de seguridad",
"interval_unit": "Unidad de intervalo",
"warning_days": "Días de aviso antes del vencimiento",
"go_back": "Volver"
"go_back": "Volver",
"auto_complete_on_recovery": "Completar automáticamente cuando el sensor se recupere",
"trigger_baseline_value": "Valor inicial (opcional)"
},
"data_description": {
"trigger_target_value": "Activar mantenimiento cuando se alcance este valor acumulado.",
@@ -1039,7 +1049,8 @@
"interval_days": "Intervalo de seguridad",
"interval_unit": "Unidad de intervalo",
"warning_days": "Días de aviso antes del vencimiento",
"go_back": "Volver"
"go_back": "Volver",
"auto_complete_on_recovery": "Completar automáticamente cuando el sensor se recupere"
},
"data_description": {
"trigger_from_state": "Solo contar transiciones desde este estado. Dejar vacío para cualquiera.",
@@ -1059,7 +1070,8 @@
"interval_days": "Intervalo de seguridad",
"interval_unit": "Unidad de intervalo",
"warning_days": "Días de aviso antes del vencimiento",
"go_back": "Volver"
"go_back": "Volver",
"auto_complete_on_recovery": "Completar automáticamente cuando el sensor se recupere"
},
"data_description": {
"trigger_runtime_hours": "Activar mantenimiento después de estas horas de tiempo de ejecución acumulado.",
@@ -1500,6 +1512,10 @@
"reading_value": {
"name": "Valor de lectura",
"description": "Valor registrado para tareas de lectura (lectura de contador, control de nivel)."
},
"completed_by": {
"name": "Completado por",
"description": "Persona que realizó el mantenimiento (debe estar vinculada a un usuario de Home Assistant). Si se omite, se registra el usuario que activó la llamada."
}
}
},
@@ -1689,6 +1705,14 @@
"labels": {
"name": "Etiquetas",
"description": "Lista de etiquetas para filtrado e informes."
},
"responsible_user": {
"name": "Usuario responsable",
"description": "Asigna la tarea a esta persona (debe estar vinculada a un usuario de Home Assistant)."
},
"clear_responsible_user": {
"name": "Quitar responsable",
"description": "Elimina la asignación actual. No se puede combinar con «Usuario responsable»."
}
}
},
@@ -230,7 +230,8 @@
"interval_days": "Turva-aikaväli",
"interval_unit": "Aikavälin yksikkö",
"warning_days": "Varoituspäivät ennen eräpäivää",
"go_back": "Palaa takaisin"
"go_back": "Palaa takaisin",
"auto_complete_on_recovery": "Suorita automaattisesti, kun anturi palautuu"
},
"data_description": {
"trigger_above": "Laukaise huolto, kun arvo ylittää tämän kynnysarvon.",
@@ -251,7 +252,9 @@
"interval_days": "Turva-aikaväli",
"interval_unit": "Aikavälin yksikkö",
"warning_days": "Varoituspäivät ennen eräpäivää",
"go_back": "Palaa takaisin"
"go_back": "Palaa takaisin",
"auto_complete_on_recovery": "Suorita automaattisesti, kun anturi palautuu",
"trigger_baseline_value": "Aloitusarvo (valinnainen)"
},
"data_description": {
"trigger_target_value": "Laukaise huolto, kun tämä kertynyt arvo saavutetaan.",
@@ -272,7 +275,8 @@
"interval_days": "Turva-aikaväli",
"interval_unit": "Aikavälin yksikkö",
"warning_days": "Varoituspäivät ennen eräpäivää",
"go_back": "Palaa takaisin"
"go_back": "Palaa takaisin",
"auto_complete_on_recovery": "Suorita automaattisesti, kun anturi palautuu"
},
"data_description": {
"trigger_from_state": "Laske vain siirtymät tästä tilasta. Jätä tyhjäksi, jos mikä tahansa käy.",
@@ -293,7 +297,8 @@
"interval_days": "Turva-aikaväli",
"interval_unit": "Aikavälin yksikkö",
"warning_days": "Varoituspäivät ennen eräpäivää",
"go_back": "Palaa takaisin"
"go_back": "Palaa takaisin",
"auto_complete_on_recovery": "Suorita automaattisesti, kun anturi palautuu"
},
"data_description": {
"trigger_runtime_hours": "Laukaise huolto tämän kertyneen käyntiaikatuntimäärän jälkeen.",
@@ -309,7 +314,8 @@
"description": "Valitse, miten useat ehdot yhdistetään.",
"data": {
"compound_logic": "Logiikka",
"go_back": "Palaa takaisin"
"go_back": "Palaa takaisin",
"auto_complete_on_recovery": "Suorita automaattisesti, kun anturi palautuu"
},
"data_description": {
"compound_logic": "JA = kaikkien ehtojen on lauettava. TAI = mikä tahansa ehto laukaisee.",
@@ -720,7 +726,8 @@
"description": "Valitse, miten useat ehdot yhdistetään.",
"data": {
"compound_logic": "Logiikka",
"go_back": "Palaa takaisin"
"go_back": "Palaa takaisin",
"auto_complete_on_recovery": "Suorita automaattisesti, kun anturi palautuu"
},
"data_description": {
"compound_logic": "JA = kaikkien ehtojen on lauettava. TAI = mikä tahansa ehto laukaisee."
@@ -999,7 +1006,8 @@
"interval_days": "Turva-aikaväli",
"interval_unit": "Aikavälin yksikkö",
"warning_days": "Varoituspäivät ennen eräpäivää",
"go_back": "Palaa takaisin"
"go_back": "Palaa takaisin",
"auto_complete_on_recovery": "Suorita automaattisesti, kun anturi palautuu"
},
"data_description": {
"trigger_above": "Laukaise huolto, kun arvo ylittää tämän kynnysarvon.",
@@ -1019,7 +1027,9 @@
"interval_days": "Turva-aikaväli",
"interval_unit": "Aikavälin yksikkö",
"warning_days": "Varoituspäivät ennen eräpäivää",
"go_back": "Palaa takaisin"
"go_back": "Palaa takaisin",
"auto_complete_on_recovery": "Suorita automaattisesti, kun anturi palautuu",
"trigger_baseline_value": "Aloitusarvo (valinnainen)"
},
"data_description": {
"trigger_target_value": "Laukaise huolto, kun tämä kertynyt arvo saavutetaan.",
@@ -1039,7 +1049,8 @@
"interval_days": "Turva-aikaväli",
"interval_unit": "Aikavälin yksikkö",
"warning_days": "Varoituspäivät ennen eräpäivää",
"go_back": "Palaa takaisin"
"go_back": "Palaa takaisin",
"auto_complete_on_recovery": "Suorita automaattisesti, kun anturi palautuu"
},
"data_description": {
"trigger_from_state": "Laske vain siirtymät tästä tilasta. Jätä tyhjäksi, jos mikä tahansa käy.",
@@ -1059,7 +1070,8 @@
"interval_days": "Turva-aikaväli",
"interval_unit": "Aikavälin yksikkö",
"warning_days": "Varoituspäivät ennen eräpäivää",
"go_back": "Palaa takaisin"
"go_back": "Palaa takaisin",
"auto_complete_on_recovery": "Suorita automaattisesti, kun anturi palautuu"
},
"data_description": {
"trigger_runtime_hours": "Laukaise huolto tämän kertyneen käyntiaikatuntimäärän jälkeen.",
@@ -1500,6 +1512,10 @@
"reading_value": {
"name": "Lukema",
"description": "Kirjattu arvo lukematehtäville (mittarilukema, tason tarkistus)."
},
"completed_by": {
"name": "Suorittaja",
"description": "Huollon suorittanut henkilö (on oltava liitetty Home Assistant -käyttäjään). Jos jätetään pois, kirjataan kutsun käynnistänyt käyttäjä."
}
}
},
@@ -1689,6 +1705,14 @@
"labels": {
"name": "Tunnisteet",
"description": "Luettelo tunnisteista suodatusta ja raportointia varten."
},
"responsible_user": {
"name": "Vastuukäyttäjä",
"description": "Määrittää tehtävän tälle henkilölle (on oltava liitetty Home Assistant -käyttäjään)."
},
"clear_responsible_user": {
"name": "Poista vastuuhenkilö",
"description": "Poistaa nykyisen määrityksen. Ei voi yhdistää kohtaan \"Vastuukäyttäjä\"."
}
}
},
@@ -230,7 +230,8 @@
"interval_days": "Intervalle de sécurité",
"interval_unit": "Unité d'intervalle",
"warning_days": "Jours d'avertissement avant échéance",
"go_back": "Retour"
"go_back": "Retour",
"auto_complete_on_recovery": "Terminer automatiquement quand le capteur se rétablit"
},
"data_description": {
"trigger_above": "Déclencher la maintenance lorsque la valeur dépasse ce seuil.",
@@ -251,7 +252,9 @@
"interval_days": "Intervalle de sécurité",
"interval_unit": "Unité d'intervalle",
"warning_days": "Jours d'avertissement avant échéance",
"go_back": "Retour"
"go_back": "Retour",
"auto_complete_on_recovery": "Terminer automatiquement quand le capteur se rétablit",
"trigger_baseline_value": "Valeur de départ (facultatif)"
},
"data_description": {
"trigger_target_value": "Déclencher la maintenance lorsque cette valeur accumulée est atteinte.",
@@ -272,7 +275,8 @@
"interval_days": "Intervalle de sécurité",
"interval_unit": "Unité d'intervalle",
"warning_days": "Jours d'avertissement avant échéance",
"go_back": "Retour"
"go_back": "Retour",
"auto_complete_on_recovery": "Terminer automatiquement quand le capteur se rétablit"
},
"data_description": {
"trigger_from_state": "Compter uniquement les transitions depuis cet état. Laissez vide pour tous.",
@@ -293,7 +297,8 @@
"interval_days": "Intervalle de sécurité",
"interval_unit": "Unité d'intervalle",
"warning_days": "Jours d'avertissement avant échéance",
"go_back": "Retour"
"go_back": "Retour",
"auto_complete_on_recovery": "Terminer automatiquement quand le capteur se rétablit"
},
"data_description": {
"trigger_runtime_hours": "Déclencher la maintenance après ce nombre d'heures de fonctionnement cumulé.",
@@ -309,7 +314,8 @@
"description": "Choisissez comment combiner les conditions multiples.",
"data": {
"compound_logic": "Logique",
"go_back": "Retour"
"go_back": "Retour",
"auto_complete_on_recovery": "Terminer automatiquement quand le capteur se rétablit"
},
"data_description": {
"compound_logic": "ET = toutes les conditions doivent se déclencher. OU = n'importe quelle condition déclenche.",
@@ -720,7 +726,8 @@
"description": "Choisissez comment combiner les conditions multiples.",
"data": {
"compound_logic": "Logique",
"go_back": "Retour"
"go_back": "Retour",
"auto_complete_on_recovery": "Terminer automatiquement quand le capteur se rétablit"
},
"data_description": {
"compound_logic": "ET = toutes les conditions doivent se déclencher. OU = n'importe quelle condition déclenche."
@@ -999,7 +1006,8 @@
"interval_days": "Intervalle de sécurité",
"interval_unit": "Unité d'intervalle",
"warning_days": "Jours d'avertissement avant échéance",
"go_back": "Retour"
"go_back": "Retour",
"auto_complete_on_recovery": "Terminer automatiquement quand le capteur se rétablit"
},
"data_description": {
"trigger_above": "Déclencher la maintenance lorsque la valeur dépasse ce seuil.",
@@ -1019,7 +1027,9 @@
"interval_days": "Intervalle de sécurité",
"interval_unit": "Unité d'intervalle",
"warning_days": "Jours d'avertissement avant échéance",
"go_back": "Retour"
"go_back": "Retour",
"auto_complete_on_recovery": "Terminer automatiquement quand le capteur se rétablit",
"trigger_baseline_value": "Valeur de départ (facultatif)"
},
"data_description": {
"trigger_target_value": "Déclencher la maintenance lorsque cette valeur accumulée est atteinte.",
@@ -1039,7 +1049,8 @@
"interval_days": "Intervalle de sécurité",
"interval_unit": "Unité d'intervalle",
"warning_days": "Jours d'avertissement avant échéance",
"go_back": "Retour"
"go_back": "Retour",
"auto_complete_on_recovery": "Terminer automatiquement quand le capteur se rétablit"
},
"data_description": {
"trigger_from_state": "Ne compter que les transitions depuis cet état. Laisser vide pour tous.",
@@ -1059,7 +1070,8 @@
"interval_days": "Intervalle de sécurité",
"interval_unit": "Unité d'intervalle",
"warning_days": "Jours d'avertissement avant échéance",
"go_back": "Retour"
"go_back": "Retour",
"auto_complete_on_recovery": "Terminer automatiquement quand le capteur se rétablit"
},
"data_description": {
"trigger_runtime_hours": "Déclencher la maintenance après ce nombre d'heures de temps de fonctionnement cumulé.",
@@ -1500,6 +1512,10 @@
"reading_value": {
"name": "Valeur relevée",
"description": "Valeur enregistrée pour les tâches de relevé (relevé de compteur, contrôle de niveau)."
},
"completed_by": {
"name": "Effectué par",
"description": "Personne ayant effectué la maintenance (doit être liée à un utilisateur Home Assistant). Si omis, l'utilisateur à l'origine de l'appel est enregistré."
}
}
},
@@ -1689,6 +1705,14 @@
"labels": {
"name": "Étiquettes",
"description": "Liste d'étiquettes pour le filtrage et les rapports."
},
"responsible_user": {
"name": "Utilisateur responsable",
"description": "Attribue la tâche à cette personne (doit être liée à un utilisateur Home Assistant)."
},
"clear_responsible_user": {
"name": "Retirer le responsable",
"description": "Supprime l'attribution actuelle. Non combinable avec « Utilisateur responsable »."
}
}
},
@@ -230,7 +230,8 @@
"interval_days": "सुरक्षा अंतराल",
"interval_unit": "अंतराल इकाई",
"warning_days": "देय होने से पहले चेतावनी दिन",
"go_back": "वापस जाएँ"
"go_back": "वापस जाएँ",
"auto_complete_on_recovery": "सेंसर ठीक होने पर स्वतः पूर्ण करें"
},
"data_description": {
"trigger_above": "मान इस थ्रेशोल्ड से अधिक होने पर रखरखाव ट्रिगर करें।",
@@ -251,7 +252,9 @@
"interval_days": "सुरक्षा अंतराल",
"interval_unit": "अंतराल इकाई",
"warning_days": "देय होने से पहले चेतावनी दिन",
"go_back": "वापस जाएँ"
"go_back": "वापस जाएँ",
"auto_complete_on_recovery": "सेंसर ठीक होने पर स्वतः पूर्ण करें",
"trigger_baseline_value": "प्रारंभिक मान (वैकल्पिक)"
},
"data_description": {
"trigger_target_value": "यह संचित मान पहुँचने पर रखरखाव ट्रिगर करें।",
@@ -272,7 +275,8 @@
"interval_days": "सुरक्षा अंतराल",
"interval_unit": "अंतराल इकाई",
"warning_days": "देय होने से पहले चेतावनी दिन",
"go_back": "वापस जाएँ"
"go_back": "वापस जाएँ",
"auto_complete_on_recovery": "सेंसर ठीक होने पर स्वतः पूर्ण करें"
},
"data_description": {
"trigger_from_state": "केवल इस स्थिति से संक्रमण गिनें। किसी भी के लिए खाली छोड़ें।",
@@ -293,7 +297,8 @@
"interval_days": "सुरक्षा अंतराल",
"interval_unit": "अंतराल इकाई",
"warning_days": "देय होने से पहले चेतावनी दिन",
"go_back": "वापस जाएँ"
"go_back": "वापस जाएँ",
"auto_complete_on_recovery": "सेंसर ठीक होने पर स्वतः पूर्ण करें"
},
"data_description": {
"trigger_runtime_hours": "इतने घंटे के संचित रनटाइम के बाद रखरखाव ट्रिगर करें।",
@@ -309,7 +314,8 @@
"description": "चुनें कि कई शर्तों को कैसे संयोजित किया जाना चाहिए।",
"data": {
"compound_logic": "तर्क",
"go_back": "वापस जाएँ"
"go_back": "वापस जाएँ",
"auto_complete_on_recovery": "सेंसर ठीक होने पर स्वतः पूर्ण करें"
},
"data_description": {
"compound_logic": "AND = सभी शर्तों का ट्रिगर होना आवश्यक है। OR = कोई भी शर्त ट्रिगर करती है।",
@@ -720,7 +726,8 @@
"description": "चुनें कि कई शर्तों को कैसे संयोजित किया जाना चाहिए।",
"data": {
"compound_logic": "तर्क",
"go_back": "वापस जाएँ"
"go_back": "वापस जाएँ",
"auto_complete_on_recovery": "सेंसर ठीक होने पर स्वतः पूर्ण करें"
},
"data_description": {
"compound_logic": "AND = सभी शर्तों का ट्रिगर होना आवश्यक है। OR = कोई भी शर्त ट्रिगर करती है।"
@@ -999,7 +1006,8 @@
"interval_days": "सुरक्षा अंतराल",
"interval_unit": "अंतराल इकाई",
"warning_days": "देय होने से पहले चेतावनी दिन",
"go_back": "वापस जाएँ"
"go_back": "वापस जाएँ",
"auto_complete_on_recovery": "सेंसर ठीक होने पर स्वतः पूर्ण करें"
},
"data_description": {
"trigger_above": "मान इस थ्रेशोल्ड से अधिक होने पर रखरखाव ट्रिगर करें।",
@@ -1019,7 +1027,9 @@
"interval_days": "सुरक्षा अंतराल",
"interval_unit": "अंतराल इकाई",
"warning_days": "देय होने से पहले चेतावनी दिन",
"go_back": "वापस जाएँ"
"go_back": "वापस जाएँ",
"auto_complete_on_recovery": "सेंसर ठीक होने पर स्वतः पूर्ण करें",
"trigger_baseline_value": "प्रारंभिक मान (वैकल्पिक)"
},
"data_description": {
"trigger_target_value": "यह संचित मान पहुँचने पर रखरखाव ट्रिगर करें।",
@@ -1039,7 +1049,8 @@
"interval_days": "सुरक्षा अंतराल",
"interval_unit": "अंतराल इकाई",
"warning_days": "देय होने से पहले चेतावनी दिन",
"go_back": "वापस जाएँ"
"go_back": "वापस जाएँ",
"auto_complete_on_recovery": "सेंसर ठीक होने पर स्वतः पूर्ण करें"
},
"data_description": {
"trigger_from_state": "केवल इस स्थिति से संक्रमण गिनें। किसी भी के लिए खाली छोड़ें।",
@@ -1059,7 +1070,8 @@
"interval_days": "सुरक्षा अंतराल",
"interval_unit": "अंतराल इकाई",
"warning_days": "देय होने से पहले चेतावनी दिन",
"go_back": "वापस जाएँ"
"go_back": "वापस जाएँ",
"auto_complete_on_recovery": "सेंसर ठीक होने पर स्वतः पूर्ण करें"
},
"data_description": {
"trigger_runtime_hours": "इतने घंटे के संचित रनटाइम के बाद रखरखाव ट्रिगर करें।",
@@ -1500,6 +1512,10 @@
"reading_value": {
"name": "रीडिंग मान",
"description": "रीडिंग कार्यों के लिए दर्ज मान (मीटर रीडिंग, स्तर जांच)।"
},
"completed_by": {
"name": "द्वारा पूर्ण",
"description": "रखरखाव करने वाला व्यक्ति (Home Assistant उपयोगकर्ता से जुड़ा होना चाहिए)। छोड़ने पर कॉल करने वाला उपयोगकर्ता दर्ज होता है।"
}
}
},
@@ -1689,6 +1705,14 @@
"labels": {
"name": "लेबल",
"description": "फ़िल्टरिंग और रिपोर्टिंग के लिए टैग की सूची।"
},
"responsible_user": {
"name": "जिम्मेदार उपयोगकर्ता",
"description": "कार्य इस व्यक्ति को सौंपें (Home Assistant उपयोगकर्ता से जुड़ा होना चाहिए)।"
},
"clear_responsible_user": {
"name": "ज़िम्मेदार हटाएँ",
"description": "वर्तमान असाइनमेंट हटाता है। \"ज़िम्मेदार उपयोगकर्ता\" के साथ संयोजित नहीं किया जा सकता।"
}
}
},
@@ -230,7 +230,8 @@
"interval_days": "Biztonsági intervallum",
"interval_unit": "Intervallum egysége",
"warning_days": "Figyelmeztetési napok az esedékesség előtt",
"go_back": "Vissza"
"go_back": "Vissza",
"auto_complete_on_recovery": "Automatikus elvégzés, ha az érzékelő helyreáll"
},
"data_description": {
"trigger_above": "A karbantartás kiváltása, ha az érték túllépi ezt a küszöböt.",
@@ -251,7 +252,9 @@
"interval_days": "Biztonsági intervallum",
"interval_unit": "Intervallum egysége",
"warning_days": "Figyelmeztetési napok az esedékesség előtt",
"go_back": "Vissza"
"go_back": "Vissza",
"auto_complete_on_recovery": "Automatikus elvégzés, ha az érzékelő helyreáll",
"trigger_baseline_value": "Kezdő leolvasás (opcionális)"
},
"data_description": {
"trigger_target_value": "A karbantartás kiváltása, amikor ez a felhalmozott érték elérésre kerül.",
@@ -272,7 +275,8 @@
"interval_days": "Biztonsági intervallum",
"interval_unit": "Intervallum egysége",
"warning_days": "Figyelmeztetési napok az esedékesség előtt",
"go_back": "Vissza"
"go_back": "Vissza",
"auto_complete_on_recovery": "Automatikus elvégzés, ha az érzékelő helyreáll"
},
"data_description": {
"trigger_from_state": "Csak az ebből az állapotból induló átmenetek számítanak. Hagyja üresen, ha bármelyik.",
@@ -293,7 +297,8 @@
"interval_days": "Biztonsági intervallum",
"interval_unit": "Intervallum egysége",
"warning_days": "Figyelmeztetési napok az esedékesség előtt",
"go_back": "Vissza"
"go_back": "Vissza",
"auto_complete_on_recovery": "Automatikus elvégzés, ha az érzékelő helyreáll"
},
"data_description": {
"trigger_runtime_hours": "A karbantartás kiváltása ennyi óra összesített üzemidő után.",
@@ -309,7 +314,8 @@
"description": "Válassza ki, hogyan legyenek a feltételek kombinálva.",
"data": {
"compound_logic": "Logika",
"go_back": "Vissza"
"go_back": "Vissza",
"auto_complete_on_recovery": "Automatikus elvégzés, ha az érzékelő helyreáll"
},
"data_description": {
"compound_logic": "ÉS = minden feltételnek teljesülnie kell. VAGY = bármely feltétel kivált.",
@@ -720,7 +726,8 @@
"description": "Válassza ki, hogyan legyenek a feltételek kombinálva.",
"data": {
"compound_logic": "Logika",
"go_back": "Vissza"
"go_back": "Vissza",
"auto_complete_on_recovery": "Automatikus elvégzés, ha az érzékelő helyreáll"
},
"data_description": {
"compound_logic": "ÉS = minden feltételnek teljesülnie kell. VAGY = bármely feltétel kivált."
@@ -999,7 +1006,8 @@
"interval_days": "Biztonsági intervallum",
"interval_unit": "Intervallum egysége",
"warning_days": "Figyelmeztetési napok az esedékesség előtt",
"go_back": "Vissza"
"go_back": "Vissza",
"auto_complete_on_recovery": "Automatikus elvégzés, ha az érzékelő helyreáll"
},
"data_description": {
"trigger_above": "A karbantartás kiváltása, ha az érték túllépi ezt a küszöböt.",
@@ -1019,7 +1027,9 @@
"interval_days": "Biztonsági intervallum",
"interval_unit": "Intervallum egysége",
"warning_days": "Figyelmeztetési napok az esedékesség előtt",
"go_back": "Vissza"
"go_back": "Vissza",
"auto_complete_on_recovery": "Automatikus elvégzés, ha az érzékelő helyreáll",
"trigger_baseline_value": "Kezdő leolvasás (opcionális)"
},
"data_description": {
"trigger_target_value": "A karbantartás kiváltása, amikor ez a felhalmozott érték elérésre kerül.",
@@ -1039,7 +1049,8 @@
"interval_days": "Biztonsági intervallum",
"interval_unit": "Intervallum egysége",
"warning_days": "Figyelmeztetési napok az esedékesség előtt",
"go_back": "Vissza"
"go_back": "Vissza",
"auto_complete_on_recovery": "Automatikus elvégzés, ha az érzékelő helyreáll"
},
"data_description": {
"trigger_from_state": "Csak az ebből az állapotból induló átmenetek számítanak. Hagyja üresen, ha bármelyik.",
@@ -1059,7 +1070,8 @@
"interval_days": "Biztonsági intervallum",
"interval_unit": "Intervallum egysége",
"warning_days": "Figyelmeztetési napok az esedékesség előtt",
"go_back": "Vissza"
"go_back": "Vissza",
"auto_complete_on_recovery": "Automatikus elvégzés, ha az érzékelő helyreáll"
},
"data_description": {
"trigger_runtime_hours": "A karbantartás kiváltása ennyi óra összesített üzemidő után.",
@@ -1500,6 +1512,10 @@
"reading_value": {
"name": "Leolvasott érték",
"description": "A leolvasás típusú feladatokhoz rögzített érték (mérőállás, szintellenőrzés)."
},
"completed_by": {
"name": "Elvégezte",
"description": "A karbantartást elvégző személy (Home Assistant-felhasználóhoz kell kapcsolódnia). Ha üres, a hívást indító felhasználó kerül rögzítésre."
}
}
},
@@ -1689,6 +1705,14 @@
"labels": {
"name": "Címkék",
"description": "Címkék listája szűréshez és jelentésekhez."
},
"responsible_user": {
"name": "Felelős felhasználó",
"description": "A feladatot ehhez a személyhez rendeli (Home Assistant-felhasználóhoz kell kapcsolódnia)."
},
"clear_responsible_user": {
"name": "Felelős törlése",
"description": "Eltávolítja a jelenlegi hozzárendelést. Nem kombinálható a „Felelős felhasználó” mezővel."
}
}
},
@@ -230,7 +230,8 @@
"interval_days": "Intervallo di sicurezza",
"interval_unit": "Unità di intervallo",
"warning_days": "Giorni di avviso prima della scadenza",
"go_back": "Indietro"
"go_back": "Indietro",
"auto_complete_on_recovery": "Completa automaticamente quando il sensore si ripristina"
},
"data_description": {
"trigger_above": "Attiva la manutenzione quando il valore supera questa soglia.",
@@ -251,7 +252,9 @@
"interval_days": "Intervallo di sicurezza",
"interval_unit": "Unità di intervallo",
"warning_days": "Giorni di avviso prima della scadenza",
"go_back": "Indietro"
"go_back": "Indietro",
"auto_complete_on_recovery": "Completa automaticamente quando il sensore si ripristina",
"trigger_baseline_value": "Valore iniziale (facoltativo)"
},
"data_description": {
"trigger_target_value": "Attiva la manutenzione quando questo valore accumulato viene raggiunto.",
@@ -272,7 +275,8 @@
"interval_days": "Intervallo di sicurezza",
"interval_unit": "Unità di intervallo",
"warning_days": "Giorni di avviso prima della scadenza",
"go_back": "Indietro"
"go_back": "Indietro",
"auto_complete_on_recovery": "Completa automaticamente quando il sensore si ripristina"
},
"data_description": {
"trigger_from_state": "Conta solo le transizioni da questo stato. Lasciare vuoto per tutti.",
@@ -293,7 +297,8 @@
"interval_days": "Intervallo di sicurezza",
"interval_unit": "Unità di intervallo",
"warning_days": "Giorni di avviso prima della scadenza",
"go_back": "Indietro"
"go_back": "Indietro",
"auto_complete_on_recovery": "Completa automaticamente quando il sensore si ripristina"
},
"data_description": {
"trigger_runtime_hours": "Attiva la manutenzione dopo questo numero di ore di funzionamento cumulato.",
@@ -309,7 +314,8 @@
"description": "Scegli come combinare più condizioni.",
"data": {
"compound_logic": "Logica",
"go_back": "Indietro"
"go_back": "Indietro",
"auto_complete_on_recovery": "Completa automaticamente quando il sensore si ripristina"
},
"data_description": {
"compound_logic": "E = tutte le condizioni devono attivarsi. O = qualsiasi condizione attiva.",
@@ -720,7 +726,8 @@
"description": "Scegli come combinare più condizioni.",
"data": {
"compound_logic": "Logica",
"go_back": "Indietro"
"go_back": "Indietro",
"auto_complete_on_recovery": "Completa automaticamente quando il sensore si ripristina"
},
"data_description": {
"compound_logic": "E = tutte le condizioni devono attivarsi. O = qualsiasi condizione attiva."
@@ -999,7 +1006,8 @@
"interval_days": "Intervallo di sicurezza",
"interval_unit": "Unità di intervallo",
"warning_days": "Giorni di avviso prima della scadenza",
"go_back": "Indietro"
"go_back": "Indietro",
"auto_complete_on_recovery": "Completa automaticamente quando il sensore si ripristina"
},
"data_description": {
"trigger_above": "Attivare la manutenzione quando il valore supera questa soglia.",
@@ -1019,7 +1027,9 @@
"interval_days": "Intervallo di sicurezza",
"interval_unit": "Unità di intervallo",
"warning_days": "Giorni di avviso prima della scadenza",
"go_back": "Indietro"
"go_back": "Indietro",
"auto_complete_on_recovery": "Completa automaticamente quando il sensore si ripristina",
"trigger_baseline_value": "Valore iniziale (facoltativo)"
},
"data_description": {
"trigger_target_value": "Attivare la manutenzione quando questo valore accumulato viene raggiunto.",
@@ -1039,7 +1049,8 @@
"interval_days": "Intervallo di sicurezza",
"interval_unit": "Unità di intervallo",
"warning_days": "Giorni di avviso prima della scadenza",
"go_back": "Indietro"
"go_back": "Indietro",
"auto_complete_on_recovery": "Completa automaticamente quando il sensore si ripristina"
},
"data_description": {
"trigger_from_state": "Contare solo le transizioni da questo stato. Lasciare vuoto per qualsiasi.",
@@ -1059,7 +1070,8 @@
"interval_days": "Intervallo di sicurezza",
"interval_unit": "Unità di intervallo",
"warning_days": "Giorni di avviso prima della scadenza",
"go_back": "Indietro"
"go_back": "Indietro",
"auto_complete_on_recovery": "Completa automaticamente quando il sensore si ripristina"
},
"data_description": {
"trigger_runtime_hours": "Attivare la manutenzione dopo questo numero di ore di tempo di esecuzione accumulato.",
@@ -1500,6 +1512,10 @@
"reading_value": {
"name": "Valore letto",
"description": "Valore registrato per attività di lettura (lettura contatore, controllo livello)."
},
"completed_by": {
"name": "Completato da",
"description": "Persona che ha eseguito la manutenzione (deve essere collegata a un utente Home Assistant). Se omesso, viene registrato l'utente che ha attivato la chiamata."
}
}
},
@@ -1689,6 +1705,14 @@
"labels": {
"name": "Etichette",
"description": "Elenco di tag per filtri e report."
},
"responsible_user": {
"name": "Utente responsabile",
"description": "Assegna l'attività a questa persona (deve essere collegata a un utente Home Assistant)."
},
"clear_responsible_user": {
"name": "Rimuovi responsabile",
"description": "Rimuove l'assegnazione attuale. Non combinabile con «Utente responsabile»."
}
}
},
@@ -230,7 +230,8 @@
"interval_days": "安全間隔",
"interval_unit": "間隔の単位",
"warning_days": "期限前の警告日数",
"go_back": "戻る"
"go_back": "戻る",
"auto_complete_on_recovery": "センサー回復時に自動完了"
},
"data_description": {
"trigger_above": "値がこのしきい値を超えたときにメンテナンスをトリガーします。",
@@ -251,7 +252,9 @@
"interval_days": "安全間隔",
"interval_unit": "間隔の単位",
"warning_days": "期限前の警告日数",
"go_back": "戻る"
"go_back": "戻る",
"auto_complete_on_recovery": "センサー回復時に自動完了",
"trigger_baseline_value": "開始値(任意)"
},
"data_description": {
"trigger_target_value": "この累積値に達したときにメンテナンスをトリガーします。",
@@ -272,7 +275,8 @@
"interval_days": "安全間隔",
"interval_unit": "間隔の単位",
"warning_days": "期限前の警告日数",
"go_back": "戻る"
"go_back": "戻る",
"auto_complete_on_recovery": "センサー回復時に自動完了"
},
"data_description": {
"trigger_from_state": "この状態からの遷移のみをカウントします。任意の状態の場合は空欄にします。",
@@ -293,7 +297,8 @@
"interval_days": "安全間隔",
"interval_unit": "間隔の単位",
"warning_days": "期限前の警告日数",
"go_back": "戻る"
"go_back": "戻る",
"auto_complete_on_recovery": "センサー回復時に自動完了"
},
"data_description": {
"trigger_runtime_hours": "累積稼働時間がこの時間数に達したらメンテナンスをトリガーします。",
@@ -309,7 +314,8 @@
"description": "複数の条件をどのように組み合わせるかを選択してください。",
"data": {
"compound_logic": "論理",
"go_back": "戻る"
"go_back": "戻る",
"auto_complete_on_recovery": "センサー回復時に自動完了"
},
"data_description": {
"compound_logic": "AND = すべての条件がトリガーされる必要があります。OR = いずれかの条件でトリガーされます。",
@@ -720,7 +726,8 @@
"description": "複数の条件をどのように組み合わせるかを選択してください。",
"data": {
"compound_logic": "論理",
"go_back": "戻る"
"go_back": "戻る",
"auto_complete_on_recovery": "センサー回復時に自動完了"
},
"data_description": {
"compound_logic": "AND = すべての条件がトリガーされる必要があります。OR = いずれかの条件でトリガーされます。"
@@ -999,7 +1006,8 @@
"interval_days": "安全間隔",
"interval_unit": "間隔の単位",
"warning_days": "期限前の警告日数",
"go_back": "戻る"
"go_back": "戻る",
"auto_complete_on_recovery": "センサー回復時に自動完了"
},
"data_description": {
"trigger_above": "値がこのしきい値を超えたときにメンテナンスをトリガーします。",
@@ -1019,7 +1027,9 @@
"interval_days": "安全間隔",
"interval_unit": "間隔の単位",
"warning_days": "期限前の警告日数",
"go_back": "戻る"
"go_back": "戻る",
"auto_complete_on_recovery": "センサー回復時に自動完了",
"trigger_baseline_value": "開始値(任意)"
},
"data_description": {
"trigger_target_value": "この累積値に達したときにメンテナンスをトリガーします。",
@@ -1039,7 +1049,8 @@
"interval_days": "安全間隔",
"interval_unit": "間隔の単位",
"warning_days": "期限前の警告日数",
"go_back": "戻る"
"go_back": "戻る",
"auto_complete_on_recovery": "センサー回復時に自動完了"
},
"data_description": {
"trigger_from_state": "この状態からの遷移のみをカウントします。任意の状態の場合は空欄にします。",
@@ -1059,7 +1070,8 @@
"interval_days": "安全間隔",
"interval_unit": "間隔の単位",
"warning_days": "期限前の警告日数",
"go_back": "戻る"
"go_back": "戻る",
"auto_complete_on_recovery": "センサー回復時に自動完了"
},
"data_description": {
"trigger_runtime_hours": "累積稼働時間がこの時間数に達したらメンテナンスをトリガーします。",
@@ -1500,6 +1512,10 @@
"reading_value": {
"name": "検針値",
"description": "検針タスクで記録する値(メーター読み取り、レベル確認)。"
},
"completed_by": {
"name": "実施者",
"description": "メンテナンスを実施した人(Home Assistant ユーザーに関連付けられている必要があります)。省略時は呼び出したユーザーが記録されます。"
}
}
},
@@ -1689,6 +1705,14 @@
"labels": {
"name": "ラベル",
"description": "フィルタリングとレポート用のタグのリスト。"
},
"responsible_user": {
"name": "担当ユーザー",
"description": "このタスクをこの人に割り当てます(Home Assistant ユーザーに関連付けられている必要があります)。"
},
"clear_responsible_user": {
"name": "担当者をクリア",
"description": "現在の割り当てを解除します。「担当ユーザー」と同時には指定できません。"
}
}
},
@@ -230,7 +230,8 @@
"interval_days": "안전 주기",
"interval_unit": "주기 단위",
"warning_days": "기한 전 경고 일수",
"go_back": "뒤로 가기"
"go_back": "뒤로 가기",
"auto_complete_on_recovery": "센서가 정상으로 돌아오면 자동 완료"
},
"data_description": {
"trigger_above": "값이 이 임계값을 초과하면 유지보수를 트리거합니다.",
@@ -251,7 +252,9 @@
"interval_days": "안전 주기",
"interval_unit": "주기 단위",
"warning_days": "기한 전 경고 일수",
"go_back": "뒤로 가기"
"go_back": "뒤로 가기",
"auto_complete_on_recovery": "센서가 정상으로 돌아오면 자동 완료",
"trigger_baseline_value": "시작 값 (선택)"
},
"data_description": {
"trigger_target_value": "이 누적 값에 도달하면 유지보수를 트리거합니다.",
@@ -272,7 +275,8 @@
"interval_days": "안전 주기",
"interval_unit": "주기 단위",
"warning_days": "기한 전 경고 일수",
"go_back": "뒤로 가기"
"go_back": "뒤로 가기",
"auto_complete_on_recovery": "센서가 정상으로 돌아오면 자동 완료"
},
"data_description": {
"trigger_from_state": "이 상태에서의 전환만 셉니다. 비워 두면 모든 상태가 포함됩니다.",
@@ -293,7 +297,8 @@
"interval_days": "안전 주기",
"interval_unit": "주기 단위",
"warning_days": "기한 전 경고 일수",
"go_back": "뒤로 가기"
"go_back": "뒤로 가기",
"auto_complete_on_recovery": "센서가 정상으로 돌아오면 자동 완료"
},
"data_description": {
"trigger_runtime_hours": "누적 가동 시간이 이 시간에 도달하면 유지보수를 트리거합니다.",
@@ -309,7 +314,8 @@
"description": "여러 조건을 결합하는 방식을 선택하세요.",
"data": {
"compound_logic": "논리",
"go_back": "뒤로 가기"
"go_back": "뒤로 가기",
"auto_complete_on_recovery": "센서가 정상으로 돌아오면 자동 완료"
},
"data_description": {
"compound_logic": "AND = 모든 조건이 트리거되어야 합니다. OR = 어느 한 조건만 트리거되면 됩니다.",
@@ -720,7 +726,8 @@
"description": "여러 조건을 결합하는 방식을 선택하세요.",
"data": {
"compound_logic": "논리",
"go_back": "뒤로 가기"
"go_back": "뒤로 가기",
"auto_complete_on_recovery": "센서가 정상으로 돌아오면 자동 완료"
},
"data_description": {
"compound_logic": "AND = 모든 조건이 트리거되어야 합니다. OR = 어느 한 조건만 트리거되면 됩니다."
@@ -999,7 +1006,8 @@
"interval_days": "안전 주기",
"interval_unit": "주기 단위",
"warning_days": "기한 전 경고 일수",
"go_back": "뒤로 가기"
"go_back": "뒤로 가기",
"auto_complete_on_recovery": "센서가 정상으로 돌아오면 자동 완료"
},
"data_description": {
"trigger_above": "값이 이 임계값을 초과하면 유지보수를 트리거합니다.",
@@ -1019,7 +1027,9 @@
"interval_days": "안전 주기",
"interval_unit": "주기 단위",
"warning_days": "기한 전 경고 일수",
"go_back": "뒤로 가기"
"go_back": "뒤로 가기",
"auto_complete_on_recovery": "센서가 정상으로 돌아오면 자동 완료",
"trigger_baseline_value": "시작 값 (선택)"
},
"data_description": {
"trigger_target_value": "이 누적 값에 도달하면 유지보수를 트리거합니다.",
@@ -1039,7 +1049,8 @@
"interval_days": "안전 주기",
"interval_unit": "주기 단위",
"warning_days": "기한 전 경고 일수",
"go_back": "뒤로 가기"
"go_back": "뒤로 가기",
"auto_complete_on_recovery": "센서가 정상으로 돌아오면 자동 완료"
},
"data_description": {
"trigger_from_state": "이 상태에서의 전환만 셉니다. 비워 두면 모든 상태가 포함됩니다.",
@@ -1059,7 +1070,8 @@
"interval_days": "안전 주기",
"interval_unit": "주기 단위",
"warning_days": "기한 전 경고 일수",
"go_back": "뒤로 가기"
"go_back": "뒤로 가기",
"auto_complete_on_recovery": "센서가 정상으로 돌아오면 자동 완료"
},
"data_description": {
"trigger_runtime_hours": "누적 가동 시간이 이 시간에 도달하면 유지보수를 트리거합니다.",
@@ -1500,6 +1512,10 @@
"reading_value": {
"name": "측정값",
"description": "측정형 작업에 기록할 값입니다(계량기 값, 수위 확인 등)."
},
"completed_by": {
"name": "완료자",
"description": "정비를 수행한 사람(Home Assistant 사용자와 연결되어 있어야 함). 생략하면 호출을 실행한 사용자가 기록됩니다."
}
}
},
@@ -1689,6 +1705,14 @@
"labels": {
"name": "라벨",
"description": "필터링과 보고에 사용할 태그 목록입니다."
},
"responsible_user": {
"name": "담당 사용자",
"description": "이 사람에게 작업을 할당합니다(Home Assistant 사용자와 연결되어 있어야 함)."
},
"clear_responsible_user": {
"name": "담당자 지우기",
"description": "현재 할당을 제거합니다. \"담당 사용자\"와 함께 사용할 수 없습니다."
}
}
},
@@ -230,7 +230,8 @@
"interval_days": "Sikkerhetsintervall",
"interval_unit": "Intervallenhet",
"warning_days": "Varseldager før forfall",
"go_back": "Gå tilbake"
"go_back": "Gå tilbake",
"auto_complete_on_recovery": "Fullfør automatisk når sensoren normaliseres"
},
"data_description": {
"trigger_above": "Utløs vedlikehold når verdien overskrider denne terskelen.",
@@ -251,7 +252,9 @@
"interval_days": "Sikkerhetsintervall",
"interval_unit": "Intervallenhet",
"warning_days": "Varseldager før forfall",
"go_back": "Gå tilbake"
"go_back": "Gå tilbake",
"auto_complete_on_recovery": "Fullfør automatisk når sensoren normaliseres",
"trigger_baseline_value": "Startverdi (valgfritt)"
},
"data_description": {
"trigger_target_value": "Utløs vedlikehold når denne akkumulerte verdien nås.",
@@ -272,7 +275,8 @@
"interval_days": "Sikkerhetsintervall",
"interval_unit": "Intervallenhet",
"warning_days": "Varseldager før forfall",
"go_back": "Gå tilbake"
"go_back": "Gå tilbake",
"auto_complete_on_recovery": "Fullfør automatisk når sensoren normaliseres"
},
"data_description": {
"trigger_from_state": "Tell bare overganger fra denne tilstanden. La feltet stå tomt for hvilken som helst.",
@@ -293,7 +297,8 @@
"interval_days": "Sikkerhetsintervall",
"interval_unit": "Intervallenhet",
"warning_days": "Varseldager før forfall",
"go_back": "Gå tilbake"
"go_back": "Gå tilbake",
"auto_complete_on_recovery": "Fullfør automatisk når sensoren normaliseres"
},
"data_description": {
"trigger_runtime_hours": "Utløs vedlikehold etter så mange timer akkumulert driftstid.",
@@ -309,7 +314,8 @@
"description": "Velg hvordan flere betingelser skal kombineres.",
"data": {
"compound_logic": "Logikk",
"go_back": "Gå tilbake"
"go_back": "Gå tilbake",
"auto_complete_on_recovery": "Fullfør automatisk når sensoren normaliseres"
},
"data_description": {
"compound_logic": "OG = alle betingelser må utløses. ELLER = en hvilken som helst betingelse utløser.",
@@ -720,7 +726,8 @@
"description": "Velg hvordan flere betingelser skal kombineres.",
"data": {
"compound_logic": "Logikk",
"go_back": "Gå tilbake"
"go_back": "Gå tilbake",
"auto_complete_on_recovery": "Fullfør automatisk når sensoren normaliseres"
},
"data_description": {
"compound_logic": "OG = alle betingelser må utløses. ELLER = en hvilken som helst betingelse utløser."
@@ -999,7 +1006,8 @@
"interval_days": "Sikkerhetsintervall",
"interval_unit": "Intervallenhet",
"warning_days": "Varseldager før forfall",
"go_back": "Gå tilbake"
"go_back": "Gå tilbake",
"auto_complete_on_recovery": "Fullfør automatisk når sensoren normaliseres"
},
"data_description": {
"trigger_above": "Utløs vedlikehold når verdien overskrider denne terskelen.",
@@ -1019,7 +1027,9 @@
"interval_days": "Sikkerhetsintervall",
"interval_unit": "Intervallenhet",
"warning_days": "Varseldager før forfall",
"go_back": "Gå tilbake"
"go_back": "Gå tilbake",
"auto_complete_on_recovery": "Fullfør automatisk når sensoren normaliseres",
"trigger_baseline_value": "Startverdi (valgfritt)"
},
"data_description": {
"trigger_target_value": "Utløs vedlikehold når denne akkumulerte verdien nås.",
@@ -1039,7 +1049,8 @@
"interval_days": "Sikkerhetsintervall",
"interval_unit": "Intervallenhet",
"warning_days": "Varseldager før forfall",
"go_back": "Gå tilbake"
"go_back": "Gå tilbake",
"auto_complete_on_recovery": "Fullfør automatisk når sensoren normaliseres"
},
"data_description": {
"trigger_from_state": "Tell bare overganger fra denne tilstanden. La feltet stå tomt for hvilken som helst.",
@@ -1059,7 +1070,8 @@
"interval_days": "Sikkerhetsintervall",
"interval_unit": "Intervallenhet",
"warning_days": "Varseldager før forfall",
"go_back": "Gå tilbake"
"go_back": "Gå tilbake",
"auto_complete_on_recovery": "Fullfør automatisk når sensoren normaliseres"
},
"data_description": {
"trigger_runtime_hours": "Utløs vedlikehold etter så mange timer akkumulert driftstid.",
@@ -1500,6 +1512,10 @@
"reading_value": {
"name": "Avlest verdi",
"description": "Registrert verdi for avlesningsoppgaver (måleravlesning, nivåkontroll)."
},
"completed_by": {
"name": "Utført av",
"description": "Person som utførte vedlikeholdet (må være knyttet til en Home Assistant-bruker). Hvis utelatt registreres brukeren som utløste kallet."
}
}
},
@@ -1689,6 +1705,14 @@
"labels": {
"name": "Etiketter",
"description": "Liste over etiketter for filtrering og rapportering."
},
"responsible_user": {
"name": "Ansvarlig bruker",
"description": "Tildeler oppgaven til denne personen (må være knyttet til en Home Assistant-bruker)."
},
"clear_responsible_user": {
"name": "Fjern ansvarlig",
"description": "Fjerner gjeldende tildeling. Kan ikke kombineres med \"Ansvarlig bruker\"."
}
}
},
@@ -230,7 +230,8 @@
"interval_days": "Veiligheidsinterval",
"interval_unit": "Intervaleenheid",
"warning_days": "Waarschuwingsdagen voor deadline",
"go_back": "Terug"
"go_back": "Terug",
"auto_complete_on_recovery": "Automatisch voltooien wanneer de sensor herstelt"
},
"data_description": {
"trigger_above": "Onderhoud activeren wanneer de waarde deze drempel overschrijdt.",
@@ -251,7 +252,9 @@
"interval_days": "Veiligheidsinterval",
"interval_unit": "Intervaleenheid",
"warning_days": "Waarschuwingsdagen voor deadline",
"go_back": "Terug"
"go_back": "Terug",
"auto_complete_on_recovery": "Automatisch voltooien wanneer de sensor herstelt",
"trigger_baseline_value": "Startwaarde (optioneel)"
},
"data_description": {
"trigger_target_value": "Onderhoud activeren wanneer deze opgetelde waarde bereikt is.",
@@ -272,7 +275,8 @@
"interval_days": "Veiligheidsinterval",
"interval_unit": "Intervaleenheid",
"warning_days": "Waarschuwingsdagen voor deadline",
"go_back": "Terug"
"go_back": "Terug",
"auto_complete_on_recovery": "Automatisch voltooien wanneer de sensor herstelt"
},
"data_description": {
"trigger_from_state": "Alleen overgangen vanaf deze status tellen. Laat leeg voor elke status.",
@@ -293,7 +297,8 @@
"interval_days": "Veiligheidsinterval",
"interval_unit": "Intervaleenheid",
"warning_days": "Waarschuwingsdagen voor deadline",
"go_back": "Terug"
"go_back": "Terug",
"auto_complete_on_recovery": "Automatisch voltooien wanneer de sensor herstelt"
},
"data_description": {
"trigger_runtime_hours": "Onderhoud activeren na dit aantal uren totale looptijd.",
@@ -309,7 +314,8 @@
"description": "Kies hoe meerdere voorwaarden gecombineerd moeten worden.",
"data": {
"compound_logic": "Logica",
"go_back": "Terug"
"go_back": "Terug",
"auto_complete_on_recovery": "Automatisch voltooien wanneer de sensor herstelt"
},
"data_description": {
"compound_logic": "EN = alle voorwaarden moeten activeren. OF = elke voorwaarde activeert.",
@@ -720,7 +726,8 @@
"description": "Kies hoe meerdere voorwaarden gecombineerd moeten worden.",
"data": {
"compound_logic": "Logica",
"go_back": "Terug"
"go_back": "Terug",
"auto_complete_on_recovery": "Automatisch voltooien wanneer de sensor herstelt"
},
"data_description": {
"compound_logic": "EN = alle voorwaarden moeten activeren. OF = elke voorwaarde activeert."
@@ -999,7 +1006,8 @@
"interval_days": "Veiligheidsinterval",
"interval_unit": "Intervaleenheid",
"warning_days": "Waarschuwingsdagen voor deadline",
"go_back": "Terug"
"go_back": "Terug",
"auto_complete_on_recovery": "Automatisch voltooien wanneer de sensor herstelt"
},
"data_description": {
"trigger_above": "Onderhoud activeren wanneer de waarde deze drempel overschrijdt.",
@@ -1019,7 +1027,9 @@
"interval_days": "Veiligheidsinterval",
"interval_unit": "Intervaleenheid",
"warning_days": "Waarschuwingsdagen voor deadline",
"go_back": "Terug"
"go_back": "Terug",
"auto_complete_on_recovery": "Automatisch voltooien wanneer de sensor herstelt",
"trigger_baseline_value": "Startwaarde (optioneel)"
},
"data_description": {
"trigger_target_value": "Onderhoud activeren wanneer deze gecumuleerde waarde is bereikt.",
@@ -1039,7 +1049,8 @@
"interval_days": "Veiligheidsinterval",
"interval_unit": "Intervaleenheid",
"warning_days": "Waarschuwingsdagen voor deadline",
"go_back": "Terug"
"go_back": "Terug",
"auto_complete_on_recovery": "Automatisch voltooien wanneer de sensor herstelt"
},
"data_description": {
"trigger_from_state": "Alleen overgangen vanaf deze status tellen. Laat leeg voor alle.",
@@ -1059,7 +1070,8 @@
"interval_days": "Veiligheidsinterval",
"interval_unit": "Intervaleenheid",
"warning_days": "Waarschuwingsdagen voor deadline",
"go_back": "Terug"
"go_back": "Terug",
"auto_complete_on_recovery": "Automatisch voltooien wanneer de sensor herstelt"
},
"data_description": {
"trigger_runtime_hours": "Onderhoud activeren na dit aantal uren gecumuleerde looptijd.",
@@ -1500,6 +1512,10 @@
"reading_value": {
"name": "Meterstand",
"description": "Vastgelegde waarde voor aflees-taken (meterstand, niveaucontrole)."
},
"completed_by": {
"name": "Voltooid door",
"description": "Persoon die het onderhoud heeft uitgevoerd (moet gekoppeld zijn aan een Home Assistant-gebruiker). Indien weggelaten wordt de aanroepende gebruiker vastgelegd."
}
}
},
@@ -1689,6 +1705,14 @@
"labels": {
"name": "Labels",
"description": "Lijst met labels voor filteren en rapportage."
},
"responsible_user": {
"name": "Verantwoordelijke gebruiker",
"description": "Wijst de taak toe aan deze persoon (moet gekoppeld zijn aan een Home Assistant-gebruiker)."
},
"clear_responsible_user": {
"name": "Toewijzing verwijderen",
"description": "Verwijdert de huidige toewijzing. Niet te combineren met 'Verantwoordelijke gebruiker'."
}
}
},
@@ -230,7 +230,8 @@
"interval_days": "Interwał bezpieczeństwa",
"interval_unit": "Jednostka interwału",
"warning_days": "Dni ostrzeżenia przed terminem",
"go_back": "Wróć"
"go_back": "Wróć",
"auto_complete_on_recovery": "Zakończ automatycznie, gdy czujnik wróci do normy"
},
"data_description": {
"trigger_above": "Wyzwalaj konserwację, gdy wartość przekroczy ten próg.",
@@ -251,7 +252,9 @@
"interval_days": "Interwał bezpieczeństwa",
"interval_unit": "Jednostka interwału",
"warning_days": "Dni ostrzeżenia przed terminem",
"go_back": "Wróć"
"go_back": "Wróć",
"auto_complete_on_recovery": "Zakończ automatycznie, gdy czujnik wróci do normy",
"trigger_baseline_value": "Wartość początkowa (opcjonalnie)"
},
"data_description": {
"trigger_target_value": "Wyzwalaj konserwację, gdy ta skumulowana wartość zostanie osiągnięta.",
@@ -272,7 +275,8 @@
"interval_days": "Interwał bezpieczeństwa",
"interval_unit": "Jednostka interwału",
"warning_days": "Dni ostrzeżenia przed terminem",
"go_back": "Wróć"
"go_back": "Wróć",
"auto_complete_on_recovery": "Zakończ automatycznie, gdy czujnik wróci do normy"
},
"data_description": {
"trigger_from_state": "Zliczaj tylko przejścia z tego stanu. Pozostaw puste dla dowolnego.",
@@ -293,7 +297,8 @@
"interval_days": "Interwał bezpieczeństwa",
"interval_unit": "Jednostka interwału",
"warning_days": "Dni ostrzeżenia przed terminem",
"go_back": "Wróć"
"go_back": "Wróć",
"auto_complete_on_recovery": "Zakończ automatycznie, gdy czujnik wróci do normy"
},
"data_description": {
"trigger_runtime_hours": "Wyzwalaj konserwację po tylu godzinach skumulowanego czasu pracy.",
@@ -309,7 +314,8 @@
"description": "Wybierz, w jaki sposób wiele warunków ma być łączonych.",
"data": {
"compound_logic": "Logika",
"go_back": "Wróć"
"go_back": "Wróć",
"auto_complete_on_recovery": "Zakończ automatycznie, gdy czujnik wróci do normy"
},
"data_description": {
"compound_logic": "ORAZ = wszystkie warunki muszą zostać spełnione. LUB = wystarczy jeden warunek.",
@@ -720,7 +726,8 @@
"description": "Wybierz, w jaki sposób wiele warunków ma być łączonych.",
"data": {
"compound_logic": "Logika",
"go_back": "Wróć"
"go_back": "Wróć",
"auto_complete_on_recovery": "Zakończ automatycznie, gdy czujnik wróci do normy"
},
"data_description": {
"compound_logic": "ORAZ = wszystkie warunki muszą zostać spełnione. LUB = wystarczy jeden warunek."
@@ -999,7 +1006,8 @@
"interval_days": "Interwał bezpieczeństwa",
"interval_unit": "Jednostka interwału",
"warning_days": "Dni ostrzeżenia przed terminem",
"go_back": "Wróć"
"go_back": "Wróć",
"auto_complete_on_recovery": "Zakończ automatycznie, gdy czujnik wróci do normy"
},
"data_description": {
"trigger_above": "Wyzwalaj konserwację, gdy wartość przekroczy ten próg.",
@@ -1019,7 +1027,9 @@
"interval_days": "Interwał bezpieczeństwa",
"interval_unit": "Jednostka interwału",
"warning_days": "Dni ostrzeżenia przed terminem",
"go_back": "Wróć"
"go_back": "Wróć",
"auto_complete_on_recovery": "Zakończ automatycznie, gdy czujnik wróci do normy",
"trigger_baseline_value": "Wartość początkowa (opcjonalnie)"
},
"data_description": {
"trigger_target_value": "Wyzwalaj konserwację, gdy ta skumulowana wartość zostanie osiągnięta.",
@@ -1039,7 +1049,8 @@
"interval_days": "Interwał bezpieczeństwa",
"interval_unit": "Jednostka interwału",
"warning_days": "Dni ostrzeżenia przed terminem",
"go_back": "Wróć"
"go_back": "Wróć",
"auto_complete_on_recovery": "Zakończ automatycznie, gdy czujnik wróci do normy"
},
"data_description": {
"trigger_from_state": "Zliczaj tylko przejścia z tego stanu. Pozostaw puste dla dowolnego.",
@@ -1059,7 +1070,8 @@
"interval_days": "Interwał bezpieczeństwa",
"interval_unit": "Jednostka interwału",
"warning_days": "Dni ostrzeżenia przed terminem",
"go_back": "Wróć"
"go_back": "Wróć",
"auto_complete_on_recovery": "Zakończ automatycznie, gdy czujnik wróci do normy"
},
"data_description": {
"trigger_runtime_hours": "Wyzwalaj konserwację po tylu godzinach skumulowanego czasu pracy.",
@@ -1500,6 +1512,10 @@
"reading_value": {
"name": "Wartość odczytu",
"description": "Zarejestrowana wartość dla zadań odczytu (odczyt licznika, kontrola poziomu)."
},
"completed_by": {
"name": "Wykonane przez",
"description": "Osoba, która wykonała konserwację (musi być powiązana z użytkownikiem Home Assistant). W razie pominięcia zapisywany jest użytkownik wywołujący."
}
}
},
@@ -1689,6 +1705,14 @@
"labels": {
"name": "Etykiety",
"description": "Lista tagów do filtrowania i raportowania."
},
"responsible_user": {
"name": "Użytkownik odpowiedzialny",
"description": "Przypisuje zadanie tej osobie (musi być powiązana z użytkownikiem Home Assistant)."
},
"clear_responsible_user": {
"name": "Usuń przypisanie",
"description": "Usuwa bieżące przypisanie. Nie można łączyć z „Odpowiedzialny użytkownik”."
}
}
},
@@ -230,7 +230,8 @@
"interval_days": "Intervalo de segurança",
"interval_unit": "Unidade do intervalo",
"warning_days": "Dias de aviso antes do vencimento",
"go_back": "Voltar"
"go_back": "Voltar",
"auto_complete_on_recovery": "Concluir automaticamente quando o sensor se recuperar"
},
"data_description": {
"trigger_above": "Aciona a manutenção quando o valor ultrapassa este limite.",
@@ -251,7 +252,9 @@
"interval_days": "Intervalo de segurança",
"interval_unit": "Unidade do intervalo",
"warning_days": "Dias de aviso antes do vencimento",
"go_back": "Voltar"
"go_back": "Voltar",
"auto_complete_on_recovery": "Concluir automaticamente quando o sensor se recuperar",
"trigger_baseline_value": "Leitura inicial (opcional)"
},
"data_description": {
"trigger_target_value": "Aciona a manutenção quando este valor acumulado é atingido.",
@@ -272,7 +275,8 @@
"interval_days": "Intervalo de segurança",
"interval_unit": "Unidade do intervalo",
"warning_days": "Dias de aviso antes do vencimento",
"go_back": "Voltar"
"go_back": "Voltar",
"auto_complete_on_recovery": "Concluir automaticamente quando o sensor se recuperar"
},
"data_description": {
"trigger_from_state": "Conta apenas transições a partir deste estado. Deixe vazio para qualquer um.",
@@ -293,7 +297,8 @@
"interval_days": "Intervalo de segurança",
"interval_unit": "Unidade do intervalo",
"warning_days": "Dias de aviso antes do vencimento",
"go_back": "Voltar"
"go_back": "Voltar",
"auto_complete_on_recovery": "Concluir automaticamente quando o sensor se recuperar"
},
"data_description": {
"trigger_runtime_hours": "Aciona a manutenção após este número de horas de operação acumulada.",
@@ -309,7 +314,8 @@
"description": "Escolha como as múltiplas condições devem ser combinadas.",
"data": {
"compound_logic": "Lógica",
"go_back": "Voltar"
"go_back": "Voltar",
"auto_complete_on_recovery": "Concluir automaticamente quando o sensor se recuperar"
},
"data_description": {
"compound_logic": "E = todas as condições devem acionar. OU = qualquer condição aciona.",
@@ -720,7 +726,8 @@
"description": "Escolha como as múltiplas condições devem ser combinadas.",
"data": {
"compound_logic": "Lógica",
"go_back": "Voltar"
"go_back": "Voltar",
"auto_complete_on_recovery": "Concluir automaticamente quando o sensor se recuperar"
},
"data_description": {
"compound_logic": "E = todas as condições devem acionar. OU = qualquer condição aciona."
@@ -999,7 +1006,8 @@
"interval_days": "Intervalo de segurança",
"interval_unit": "Unidade do intervalo",
"warning_days": "Dias de aviso antes do vencimento",
"go_back": "Voltar"
"go_back": "Voltar",
"auto_complete_on_recovery": "Concluir automaticamente quando o sensor se recuperar"
},
"data_description": {
"trigger_above": "Aciona a manutenção quando o valor ultrapassa este limite.",
@@ -1019,7 +1027,9 @@
"interval_days": "Intervalo de segurança",
"interval_unit": "Unidade do intervalo",
"warning_days": "Dias de aviso antes do vencimento",
"go_back": "Voltar"
"go_back": "Voltar",
"auto_complete_on_recovery": "Concluir automaticamente quando o sensor se recuperar",
"trigger_baseline_value": "Leitura inicial (opcional)"
},
"data_description": {
"trigger_target_value": "Aciona a manutenção quando este valor acumulado é atingido.",
@@ -1039,7 +1049,8 @@
"interval_days": "Intervalo de segurança",
"interval_unit": "Unidade do intervalo",
"warning_days": "Dias de aviso antes do vencimento",
"go_back": "Voltar"
"go_back": "Voltar",
"auto_complete_on_recovery": "Concluir automaticamente quando o sensor se recuperar"
},
"data_description": {
"trigger_from_state": "Conta apenas transições a partir deste estado. Deixe vazio para qualquer um.",
@@ -1059,7 +1070,8 @@
"interval_days": "Intervalo de segurança",
"interval_unit": "Unidade do intervalo",
"warning_days": "Dias de aviso antes do vencimento",
"go_back": "Voltar"
"go_back": "Voltar",
"auto_complete_on_recovery": "Concluir automaticamente quando o sensor se recuperar"
},
"data_description": {
"trigger_runtime_hours": "Aciona a manutenção após este número de horas de operação acumulada.",
@@ -1500,6 +1512,10 @@
"reading_value": {
"name": "Valor da leitura",
"description": "Valor registrado para tarefas do tipo leitura (leitura de medidor, verificação de nível)."
},
"completed_by": {
"name": "Concluído por",
"description": "Pessoa que realizou a manutenção (deve estar vinculada a um usuário do Home Assistant). Se omitido, o usuário que acionou a chamada é registrado."
}
}
},
@@ -1689,6 +1705,14 @@
"labels": {
"name": "Etiquetas",
"description": "Lista de tags para filtragem e relatórios."
},
"responsible_user": {
"name": "Usuário responsável",
"description": "Atribui a tarefa a esta pessoa (deve estar vinculada a um usuário do Home Assistant)."
},
"clear_responsible_user": {
"name": "Remover responsável",
"description": "Remove a atribuição atual. Não pode ser combinado com \"Usuário responsável\"."
}
}
},
@@ -230,7 +230,8 @@
"interval_days": "Intervalo de segurança",
"interval_unit": "Unidade de intervalo",
"warning_days": "Dias de aviso antes do vencimento",
"go_back": "Voltar"
"go_back": "Voltar",
"auto_complete_on_recovery": "Concluir automaticamente quando o sensor recuperar"
},
"data_description": {
"trigger_above": "Acionar manutenção quando o valor exceder este limiar.",
@@ -251,7 +252,9 @@
"interval_days": "Intervalo de segurança",
"interval_unit": "Unidade de intervalo",
"warning_days": "Dias de aviso antes do vencimento",
"go_back": "Voltar"
"go_back": "Voltar",
"auto_complete_on_recovery": "Concluir automaticamente quando o sensor recuperar",
"trigger_baseline_value": "Valor inicial (opcional)"
},
"data_description": {
"trigger_target_value": "Acionar manutenção quando este valor acumulado for atingido.",
@@ -272,7 +275,8 @@
"interval_days": "Intervalo de segurança",
"interval_unit": "Unidade de intervalo",
"warning_days": "Dias de aviso antes do vencimento",
"go_back": "Voltar"
"go_back": "Voltar",
"auto_complete_on_recovery": "Concluir automaticamente quando o sensor recuperar"
},
"data_description": {
"trigger_from_state": "Contar apenas transições a partir deste estado. Deixe vazio para qualquer um.",
@@ -293,7 +297,8 @@
"interval_days": "Intervalo de segurança",
"interval_unit": "Unidade de intervalo",
"warning_days": "Dias de aviso antes do vencimento",
"go_back": "Voltar"
"go_back": "Voltar",
"auto_complete_on_recovery": "Concluir automaticamente quando o sensor recuperar"
},
"data_description": {
"trigger_runtime_hours": "Acionar manutenção após estas horas de tempo de execução acumulado.",
@@ -309,7 +314,8 @@
"description": "Escolha como múltiplas condições devem ser combinadas.",
"data": {
"compound_logic": "Lógica",
"go_back": "Voltar"
"go_back": "Voltar",
"auto_complete_on_recovery": "Concluir automaticamente quando o sensor recuperar"
},
"data_description": {
"compound_logic": "AND = todas as condições devem ser acionadas. OR = qualquer condição aciona.",
@@ -720,7 +726,8 @@
"description": "Escolha como múltiplas condições devem ser combinadas.",
"data": {
"compound_logic": "Lógica",
"go_back": "Voltar"
"go_back": "Voltar",
"auto_complete_on_recovery": "Concluir automaticamente quando o sensor recuperar"
},
"data_description": {
"compound_logic": "AND = todas as condições devem ser acionadas. OR = qualquer condição aciona."
@@ -999,7 +1006,8 @@
"interval_days": "Intervalo de segurança",
"interval_unit": "Unidade de intervalo",
"warning_days": "Dias de aviso antes do vencimento",
"go_back": "Voltar"
"go_back": "Voltar",
"auto_complete_on_recovery": "Concluir automaticamente quando o sensor recuperar"
},
"data_description": {
"trigger_above": "Acionar manutenção quando o valor exceder este limiar.",
@@ -1019,7 +1027,9 @@
"interval_days": "Intervalo de segurança",
"interval_unit": "Unidade de intervalo",
"warning_days": "Dias de aviso antes do vencimento",
"go_back": "Voltar"
"go_back": "Voltar",
"auto_complete_on_recovery": "Concluir automaticamente quando o sensor recuperar",
"trigger_baseline_value": "Valor inicial (opcional)"
},
"data_description": {
"trigger_target_value": "Acionar manutenção quando este valor acumulado for atingido.",
@@ -1039,7 +1049,8 @@
"interval_days": "Intervalo de segurança",
"interval_unit": "Unidade de intervalo",
"warning_days": "Dias de aviso antes do vencimento",
"go_back": "Voltar"
"go_back": "Voltar",
"auto_complete_on_recovery": "Concluir automaticamente quando o sensor recuperar"
},
"data_description": {
"trigger_from_state": "Contar apenas transições a partir deste estado. Deixe vazio para qualquer um.",
@@ -1059,7 +1070,8 @@
"interval_days": "Intervalo de segurança",
"interval_unit": "Unidade de intervalo",
"warning_days": "Dias de aviso antes do vencimento",
"go_back": "Voltar"
"go_back": "Voltar",
"auto_complete_on_recovery": "Concluir automaticamente quando o sensor recuperar"
},
"data_description": {
"trigger_runtime_hours": "Acionar manutenção após estas horas de tempo de execução acumulado.",
@@ -1500,6 +1512,10 @@
"reading_value": {
"name": "Valor de leitura",
"description": "Valor registado para tarefas de leitura (leitura de contador, verificação de nível)."
},
"completed_by": {
"name": "Concluído por",
"description": "Pessoa que realizou a manutenção (tem de estar associada a um utilizador do Home Assistant). Se omitido, é registado o utilizador que acionou a chamada."
}
}
},
@@ -1689,6 +1705,14 @@
"labels": {
"name": "Etiquetas",
"description": "Lista de etiquetas para filtragem e relatórios."
},
"responsible_user": {
"name": "Utilizador responsável",
"description": "Atribui a tarefa a esta pessoa (tem de estar associada a um utilizador do Home Assistant)."
},
"clear_responsible_user": {
"name": "Remover responsável",
"description": "Remove a atribuição atual. Não pode ser combinado com «Utilizador responsável»."
}
}
},
@@ -230,7 +230,8 @@
"interval_days": "Защитный интервал",
"interval_unit": "Единица интервала",
"warning_days": "Дней предупреждения до срока",
"go_back": "Назад"
"go_back": "Назад",
"auto_complete_on_recovery": "Автозавершение при восстановлении датчика"
},
"data_description": {
"trigger_above": "Запускать обслуживание, когда значение превышает этот порог.",
@@ -251,7 +252,9 @@
"interval_days": "Защитный интервал",
"interval_unit": "Единица интервала",
"warning_days": "Дней предупреждения до срока",
"go_back": "Назад"
"go_back": "Назад",
"auto_complete_on_recovery": "Автозавершение при восстановлении датчика",
"trigger_baseline_value": "Начальное значение (необязательно)"
},
"data_description": {
"trigger_target_value": "Запускать обслуживание при достижении этого накопленного значения.",
@@ -272,7 +275,8 @@
"interval_days": "Защитный интервал",
"interval_unit": "Единица интервала",
"warning_days": "Дней предупреждения до срока",
"go_back": "Назад"
"go_back": "Назад",
"auto_complete_on_recovery": "Автозавершение при восстановлении датчика"
},
"data_description": {
"trigger_from_state": "Учитывать только переходы из этого состояния. Оставьте пустым для любого.",
@@ -293,7 +297,8 @@
"interval_days": "Защитный интервал",
"interval_unit": "Единица интервала",
"warning_days": "Дней предупреждения до срока",
"go_back": "Назад"
"go_back": "Назад",
"auto_complete_on_recovery": "Автозавершение при восстановлении датчика"
},
"data_description": {
"trigger_runtime_hours": "Запускать обслуживание после этого количества часов накопленного времени работы.",
@@ -309,7 +314,8 @@
"description": "Выберите, как должны комбинироваться несколько условий.",
"data": {
"compound_logic": "Логика",
"go_back": "Назад"
"go_back": "Назад",
"auto_complete_on_recovery": "Автозавершение при восстановлении датчика"
},
"data_description": {
"compound_logic": "И = все условия должны сработать. ИЛИ = любое условие срабатывает.",
@@ -720,7 +726,8 @@
"description": "Выберите, как должны комбинироваться несколько условий.",
"data": {
"compound_logic": "Логика",
"go_back": "Назад"
"go_back": "Назад",
"auto_complete_on_recovery": "Автозавершение при восстановлении датчика"
},
"data_description": {
"compound_logic": "И = все условия должны сработать. ИЛИ = любое условие срабатывает."
@@ -999,7 +1006,8 @@
"interval_days": "Защитный интервал",
"interval_unit": "Единица интервала",
"warning_days": "Дней предупреждения до срока",
"go_back": "Назад"
"go_back": "Назад",
"auto_complete_on_recovery": "Автозавершение при восстановлении датчика"
},
"data_description": {
"trigger_above": "Запускать обслуживание, когда значение превышает этот порог.",
@@ -1019,7 +1027,9 @@
"interval_days": "Защитный интервал",
"interval_unit": "Единица интервала",
"warning_days": "Дней предупреждения до срока",
"go_back": "Назад"
"go_back": "Назад",
"auto_complete_on_recovery": "Автозавершение при восстановлении датчика",
"trigger_baseline_value": "Начальное значение (необязательно)"
},
"data_description": {
"trigger_target_value": "Запускать обслуживание при достижении этого накопленного значения.",
@@ -1039,7 +1049,8 @@
"interval_days": "Защитный интервал",
"interval_unit": "Единица интервала",
"warning_days": "Дней предупреждения до срока",
"go_back": "Назад"
"go_back": "Назад",
"auto_complete_on_recovery": "Автозавершение при восстановлении датчика"
},
"data_description": {
"trigger_from_state": "Учитывать только переходы из этого состояния. Оставьте пустым для любого.",
@@ -1059,7 +1070,8 @@
"interval_days": "Защитный интервал",
"interval_unit": "Единица интервала",
"warning_days": "Дней предупреждения до срока",
"go_back": "Назад"
"go_back": "Назад",
"auto_complete_on_recovery": "Автозавершение при восстановлении датчика"
},
"data_description": {
"trigger_runtime_hours": "Запускать обслуживание после этого количества часов накопленного времени работы.",
@@ -1500,6 +1512,10 @@
"reading_value": {
"name": "Показание",
"description": "Записанное значение для задач снятия показаний (показание счётчика, проверка уровня)."
},
"completed_by": {
"name": "Кем выполнено",
"description": "Человек, выполнивший обслуживание (должен быть связан с пользователем Home Assistant). Если не указано, записывается пользователь, вызвавший службу."
}
}
},
@@ -1689,6 +1705,14 @@
"labels": {
"name": "Метки",
"description": "Список меток для фильтрации и отчётов."
},
"responsible_user": {
"name": "Ответственный пользователь",
"description": "Назначает задачу этому человеку (должен быть связан с пользователем Home Assistant)."
},
"clear_responsible_user": {
"name": "Снять назначение",
"description": "Снимает текущее назначение. Нельзя сочетать с «Ответственный пользователь»."
}
}
},
@@ -230,7 +230,8 @@
"interval_days": "Säkerhetsintervall",
"interval_unit": "Intervallenhet",
"warning_days": "Dagar för varning före förfall",
"go_back": "Tillbaka"
"go_back": "Tillbaka",
"auto_complete_on_recovery": "Slutför automatiskt när sensorn återhämtar sig"
},
"data_description": {
"trigger_above": "Utlös underhåll när värdet överskrider denna tröskel.",
@@ -251,7 +252,9 @@
"interval_days": "Säkerhetsintervall",
"interval_unit": "Intervallenhet",
"warning_days": "Dagar för varning före förfall",
"go_back": "Tillbaka"
"go_back": "Tillbaka",
"auto_complete_on_recovery": "Slutför automatiskt när sensorn återhämtar sig",
"trigger_baseline_value": "Startvärde (valfritt)"
},
"data_description": {
"trigger_target_value": "Utlös underhåll när detta ackumulerade värde nås.",
@@ -272,7 +275,8 @@
"interval_days": "Säkerhetsintervall",
"interval_unit": "Intervallenhet",
"warning_days": "Dagar för varning före förfall",
"go_back": "Tillbaka"
"go_back": "Tillbaka",
"auto_complete_on_recovery": "Slutför automatiskt när sensorn återhämtar sig"
},
"data_description": {
"trigger_from_state": "Räkna endast övergångar från detta tillstånd. Lämna tomt för alla.",
@@ -293,7 +297,8 @@
"interval_days": "Säkerhetsintervall",
"interval_unit": "Intervallenhet",
"warning_days": "Dagar för varning före förfall",
"go_back": "Tillbaka"
"go_back": "Tillbaka",
"auto_complete_on_recovery": "Slutför automatiskt när sensorn återhämtar sig"
},
"data_description": {
"trigger_runtime_hours": "Utlös underhåll efter så här många timmar ackumulerad körtid.",
@@ -309,7 +314,8 @@
"description": "Välj hur flera villkor ska kombineras.",
"data": {
"compound_logic": "Logik",
"go_back": "Tillbaka"
"go_back": "Tillbaka",
"auto_complete_on_recovery": "Slutför automatiskt när sensorn återhämtar sig"
},
"data_description": {
"compound_logic": "AND = alla villkor måste utlösa. OR = vilket som helst villkor utlöser.",
@@ -720,7 +726,8 @@
"description": "Välj hur flera villkor ska kombineras.",
"data": {
"compound_logic": "Logik",
"go_back": "Tillbaka"
"go_back": "Tillbaka",
"auto_complete_on_recovery": "Slutför automatiskt när sensorn återhämtar sig"
},
"data_description": {
"compound_logic": "AND = alla villkor måste utlösa. OR = vilket som helst villkor utlöser."
@@ -999,7 +1006,8 @@
"interval_days": "Säkerhetsintervall",
"interval_unit": "Intervallenhet",
"warning_days": "Dagar för varning före förfall",
"go_back": "Tillbaka"
"go_back": "Tillbaka",
"auto_complete_on_recovery": "Slutför automatiskt när sensorn återhämtar sig"
},
"data_description": {
"trigger_above": "Utlös underhåll när värdet överskrider denna tröskel.",
@@ -1019,7 +1027,9 @@
"interval_days": "Säkerhetsintervall",
"interval_unit": "Intervallenhet",
"warning_days": "Dagar för varning före förfall",
"go_back": "Tillbaka"
"go_back": "Tillbaka",
"auto_complete_on_recovery": "Slutför automatiskt när sensorn återhämtar sig",
"trigger_baseline_value": "Startvärde (valfritt)"
},
"data_description": {
"trigger_target_value": "Utlös underhåll när detta ackumulerade värde nås.",
@@ -1039,7 +1049,8 @@
"interval_days": "Säkerhetsintervall",
"interval_unit": "Intervallenhet",
"warning_days": "Dagar för varning före förfall",
"go_back": "Tillbaka"
"go_back": "Tillbaka",
"auto_complete_on_recovery": "Slutför automatiskt när sensorn återhämtar sig"
},
"data_description": {
"trigger_from_state": "Räkna endast övergångar från detta tillstånd. Lämna tomt för alla.",
@@ -1059,7 +1070,8 @@
"interval_days": "Säkerhetsintervall",
"interval_unit": "Intervallenhet",
"warning_days": "Dagar för varning före förfall",
"go_back": "Tillbaka"
"go_back": "Tillbaka",
"auto_complete_on_recovery": "Slutför automatiskt när sensorn återhämtar sig"
},
"data_description": {
"trigger_runtime_hours": "Utlös underhåll efter så här många timmar ackumulerad körtid.",
@@ -1500,6 +1512,10 @@
"reading_value": {
"name": "Avläst värde",
"description": "Registrerat värde för avläsningsuppgifter (mätaravläsning, nivåkontroll)."
},
"completed_by": {
"name": "Slutförd av",
"description": "Person som utförde underhållet (måste vara kopplad till en Home Assistant-användare). Om utelämnad registreras användaren som utlöste anropet."
}
}
},
@@ -1689,6 +1705,14 @@
"labels": {
"name": "Etiketter",
"description": "Lista med etiketter för filtrering och rapportering."
},
"responsible_user": {
"name": "Ansvarig användare",
"description": "Tilldelar uppgiften till denna person (måste vara kopplad till en Home Assistant-användare)."
},
"clear_responsible_user": {
"name": "Ta bort ansvarig",
"description": "Tar bort den aktuella tilldelningen. Kan inte kombineras med \"Ansvarig användare\"."
}
}
},
@@ -230,7 +230,8 @@
"interval_days": "Güvenlik aralığı",
"interval_unit": "Aralık birimi",
"warning_days": "Vadeden önce uyarı günleri",
"go_back": "Geri dön"
"go_back": "Geri dön",
"auto_complete_on_recovery": "Sensör düzelince otomatik tamamla"
},
"data_description": {
"trigger_above": "Değer bu eşiği aştığında bakımı tetikle.",
@@ -251,7 +252,9 @@
"interval_days": "Güvenlik aralığı",
"interval_unit": "Aralık birimi",
"warning_days": "Vadeden önce uyarı günleri",
"go_back": "Geri dön"
"go_back": "Geri dön",
"auto_complete_on_recovery": "Sensör düzelince otomatik tamamla",
"trigger_baseline_value": "Başlangıç okuması (isteğe bağlı)"
},
"data_description": {
"trigger_target_value": "Bu birikmiş değere ulaşıldığında bakımı tetikle.",
@@ -272,7 +275,8 @@
"interval_days": "Güvenlik aralığı",
"interval_unit": "Aralık birimi",
"warning_days": "Vadeden önce uyarı günleri",
"go_back": "Geri dön"
"go_back": "Geri dön",
"auto_complete_on_recovery": "Sensör düzelince otomatik tamamla"
},
"data_description": {
"trigger_from_state": "Yalnızca bu durumdan yapılan geçişleri say. Herhangi biri için boş bırakın.",
@@ -293,7 +297,8 @@
"interval_days": "Güvenlik aralığı",
"interval_unit": "Aralık birimi",
"warning_days": "Vadeden önce uyarı günleri",
"go_back": "Geri dön"
"go_back": "Geri dön",
"auto_complete_on_recovery": "Sensör düzelince otomatik tamamla"
},
"data_description": {
"trigger_runtime_hours": "Bu kadar saat birikmiş çalışma süresinden sonra bakımı tetikle.",
@@ -309,7 +314,8 @@
"description": "Birden çok koşulun nasıl birleştirileceğini seçin.",
"data": {
"compound_logic": "Mantık",
"go_back": "Geri dön"
"go_back": "Geri dön",
"auto_complete_on_recovery": "Sensör düzelince otomatik tamamla"
},
"data_description": {
"compound_logic": "VE = tüm koşullar tetiklenmelidir. VEYA = herhangi bir koşul tetikler.",
@@ -720,7 +726,8 @@
"description": "Birden çok koşulun nasıl birleştirileceğini seçin.",
"data": {
"compound_logic": "Mantık",
"go_back": "Geri dön"
"go_back": "Geri dön",
"auto_complete_on_recovery": "Sensör düzelince otomatik tamamla"
},
"data_description": {
"compound_logic": "VE = tüm koşullar tetiklenmelidir. VEYA = herhangi bir koşul tetikler."
@@ -999,7 +1006,8 @@
"interval_days": "Güvenlik aralığı",
"interval_unit": "Aralık birimi",
"warning_days": "Vadeden önce uyarı günleri",
"go_back": "Geri dön"
"go_back": "Geri dön",
"auto_complete_on_recovery": "Sensör düzelince otomatik tamamla"
},
"data_description": {
"trigger_above": "Değer bu eşiği aştığında bakımı tetikle.",
@@ -1019,7 +1027,9 @@
"interval_days": "Güvenlik aralığı",
"interval_unit": "Aralık birimi",
"warning_days": "Vadeden önce uyarı günleri",
"go_back": "Geri dön"
"go_back": "Geri dön",
"auto_complete_on_recovery": "Sensör düzelince otomatik tamamla",
"trigger_baseline_value": "Başlangıç okuması (isteğe bağlı)"
},
"data_description": {
"trigger_target_value": "Bu birikmiş değere ulaşıldığında bakımı tetikle.",
@@ -1039,7 +1049,8 @@
"interval_days": "Güvenlik aralığı",
"interval_unit": "Aralık birimi",
"warning_days": "Vadeden önce uyarı günleri",
"go_back": "Geri dön"
"go_back": "Geri dön",
"auto_complete_on_recovery": "Sensör düzelince otomatik tamamla"
},
"data_description": {
"trigger_from_state": "Yalnızca bu durumdan yapılan geçişleri say. Herhangi biri için boş bırakın.",
@@ -1059,7 +1070,8 @@
"interval_days": "Güvenlik aralığı",
"interval_unit": "Aralık birimi",
"warning_days": "Vadeden önce uyarı günleri",
"go_back": "Geri dön"
"go_back": "Geri dön",
"auto_complete_on_recovery": "Sensör düzelince otomatik tamamla"
},
"data_description": {
"trigger_runtime_hours": "Bu kadar saat birikmiş çalışma süresinden sonra bakımı tetikle.",
@@ -1500,6 +1512,10 @@
"reading_value": {
"name": "Okuma değeri",
"description": "Okuma türü görevler için kaydedilen değer (sayaç okuması, seviye kontrolü)."
},
"completed_by": {
"name": "Tamamlayan",
"description": "Bakımı yapan kişi (bir Home Assistant kullanıcısına bağlı olmalıdır). Belirtilmezse çağrıyı tetikleyen kullanıcı kaydedilir."
}
}
},
@@ -1689,6 +1705,14 @@
"labels": {
"name": "Etiketler",
"description": "Filtreleme ve raporlama için etiket listesi."
},
"responsible_user": {
"name": "Sorumlu kullanıcı",
"description": "Görevi bu kişiye atar (bir Home Assistant kullanıcısına bağlı olmalıdır)."
},
"clear_responsible_user": {
"name": "Sorumluyu Kaldır",
"description": "Mevcut atamayı kaldırır. \"Sorumlu kullanıcı\" ile birlikte kullanılamaz."
}
}
},

Some files were not shown because too many files have changed in this diff Show More