393 files

This commit is contained in:
Home Assistant Version Control
2026-09-27 17:16:54 +00:00
parent b05b7a897e
commit 39d97ac2db
404 changed files with 54006 additions and 11034 deletions
+1 -1
View File
@@ -1 +1 @@
{"pid": 71, "version": 1, "ha_version": "2026.9.3", "start_ts": 1790479477.543377}
{"pid": 71, "version": 1, "ha_version": "2026.9.3", "start_ts": 1790529350.0698402}
+3 -3
View File
@@ -26,7 +26,7 @@
},
{
"id": "ee7be1b18ae84060a5d515640f6e5739",
"url": "/seatemperatures_frontend/sea-temperatures-card.js?v=3.3.1",
"url": "/seatemperatures_frontend/sea-temperatures-card.js?v=3.4.0",
"type": "module"
},
{
@@ -41,7 +41,7 @@
},
{
"id": "0b28e2d01d0d4033b2f0a339cc7d86c1",
"url": "/ha_washdata/ha-washdata-card.js?v=dee67fee9e",
"url": "/ha_washdata/ha-washdata-card.js?v=cad53dea8b",
"type": "module"
},
{
@@ -330,7 +330,7 @@
"type": "module"
},
{
"id": "b834e10d3ff04ae3a30c6f2f4c36e33f",
"id": "7d417eee26994090af1ddcbd700cdc38",
"url": "/climate_scheduler/static/climate-scheduler-card.js?v=1.15.1",
"type": "module"
}
@@ -1 +1 @@
{"model": {"id": "LED2005R5", "name": "TRADFRI bulb GU10 345 lumen, dimmable, white spectrum", "aliases": ["TRADFRI bulb GU10 WS 345lm", "TRADFRIbulbGU10WS345lm"], "hash": "71d529708b73523b1fe33803715eff90", "device_type": "light"}, "resources": ["color_temp.csv.gz", "model.json", "color_temp.csv"]}
{"model": {"id": "LED2005R5", "name": "TRADFRI bulb GU10 345 lumen, dimmable, white spectrum", "aliases": ["TRADFRI bulb GU10 WS 345lm", "TRADFRIbulbGU10WS345lm"], "hash": "71d529708b73523b1fe33803715eff90", "device_type": "light"}, "resources": ["color_temp.csv.gz", "model.json"]}
@@ -1 +1 @@
{"model": {"id": "LED2104R3", "name": "TRADFRI bulb GU10 WW 345lm", "aliases": ["TRADFRI bulb GU10 WW 345lm"], "hash": "e441162e1939bf1f0d8fca3ef73cb10d", "device_type": "light"}, "resources": ["model.json", "brightness.csv", "brightness.csv.gz"]}
{"model": {"id": "LED2104R3", "name": "TRADFRI bulb GU10 WW 345lm", "aliases": ["TRADFRI bulb GU10 WW 345lm"], "hash": "e441162e1939bf1f0d8fca3ef73cb10d", "device_type": "light"}, "resources": ["model.json", "brightness.csv.gz"]}
@@ -1 +1 @@
{"model": {"id": "LED2106R3", "name": "TRADFRI bulb GU10 WS 345 lm, dimmable, white spectrum", "hash": "006369bd1cad56cf521aace09c2c5f03", "device_type": "light"}, "resources": ["color_temp.csv.gz", "model.json", "color_temp.csv"]}
{"model": {"id": "LED2106R3", "name": "TRADFRI bulb GU10 WS 345 lm, dimmable, white spectrum", "hash": "006369bd1cad56cf521aace09c2c5f03", "device_type": "light"}, "resources": ["color_temp.csv.gz", "model.json"]}
@@ -1 +1 @@
{"model": {"id": "9290022656", "name": "A19 E26 RGB + Tunable White Bulb (8.8W, 800 lm)", "aliases": ["SHRGB1C"], "hash": "819ff5beda4281f2eeac7bb6ac6492a4", "device_type": "light"}, "resources": ["color_temp.csv.gz", "model.json", "color_temp.csv", "hs.csv.gz", "hs.csv"]}
{"model": {"id": "9290022656", "name": "A19 E26 RGB + Tunable White Bulb (8.8W, 800 lm)", "aliases": ["SHRGB1C"], "hash": "819ff5beda4281f2eeac7bb6ac6492a4", "device_type": "light"}, "resources": ["color_temp.csv.gz", "model.json", "hs.csv.gz"]}
@@ -1 +1 @@
{"model": {"id": "9290022662", "name": "ST19 E26 Clear Filament Bulb (3.8W, 350 lm)", "aliases": ["SHDW9"], "hash": "e9bd815f07e198ac47ebdfdd4144a767", "device_type": "light"}, "resources": ["model.json", "brightness.csv", "brightness.csv.gz"]}
{"model": {"id": "9290022662", "name": "ST19 E26 Clear Filament Bulb (3.8W, 350 lm)", "aliases": ["SHDW9"], "hash": "e9bd815f07e198ac47ebdfdd4144a767", "device_type": "light"}, "resources": ["model.json", "brightness.csv.gz"]}
@@ -1 +1 @@
{"model": {"id": "9290023839", "name": "G95 E27 RGB + Tunable White Bulb (11W, 1055 lm)", "aliases": ["SHRGB1C"], "hash": "0b41c5c8b655fd18bcb267b39740ecf2", "device_type": "light"}, "resources": ["color_temp.csv.gz", "model.json", "color_temp.csv", "hs.csv.gz", "hs.csv"]}
{"model": {"id": "9290023839", "name": "G95 E27 RGB + Tunable White Bulb (11W, 1055 lm)", "aliases": ["SHRGB1C"], "hash": "0b41c5c8b655fd18bcb267b39740ecf2", "device_type": "light"}, "resources": ["color_temp.csv.gz", "model.json", "hs.csv.gz"]}
@@ -1 +1 @@
{"model": {"id": "929003294819", "name": "3.5-inch Recessed Downlight (8 W, 800 lm)", "aliases": ["SHRGB"], "legacy_ids": ["WIZ294819"], "hash": "07058011e94e5b7a2373f3c2a43156b9", "device_type": "light"}, "resources": ["color_temp.csv.gz", "model.json", "color_temp.csv", "hs.csv.gz", "hs.csv"]}
{"model": {"id": "929003294819", "name": "3.5-inch Recessed Downlight (8 W, 800 lm)", "aliases": ["SHRGB"], "legacy_ids": ["WIZ294819"], "hash": "07058011e94e5b7a2373f3c2a43156b9", "device_type": "light"}, "resources": ["color_temp.csv.gz", "model.json", "hs.csv.gz"]}
@@ -1 +1 @@
{"model": {"id": "929003500001", "name": "Smart Wi-Fi Full-Color RGB + Tunable White LED Bulb (A80, E27, 18.5 W / 2452 lm)", "aliases": ["SHRGB"], "legacy_ids": ["SHRGB"], "hash": "c45d0a18ddad0039b79330dbfc4afa6d", "device_type": "light"}, "resources": ["color_temp.csv.gz", "model.json", "color_temp.csv", "hs.csv.gz", "hs.csv"]}
{"model": {"id": "929003500001", "name": "Smart Wi-Fi Full-Color RGB + Tunable White LED Bulb (A80, E27, 18.5 W / 2452 lm)", "aliases": ["SHRGB"], "legacy_ids": ["SHRGB"], "hash": "c45d0a18ddad0039b79330dbfc4afa6d", "device_type": "light"}, "resources": ["color_temp.csv.gz", "model.json", "hs.csv.gz"]}
@@ -1 +1 @@
{"model": {"id": "9290036010", "name": "A60 E27 RGB + Tunable White Bulb (8.5W, 806 lm)", "aliases": ["SHRGB"], "hash": "14d7feb5203fee810a4ad117d1277607", "device_type": "light"}, "resources": ["color_temp.csv.gz", "model.json", "color_temp.csv", "hs.csv.gz", "hs.csv"]}
{"model": {"id": "9290036010", "name": "A60 E27 RGB + Tunable White Bulb (8.5W, 806 lm)", "aliases": ["SHRGB"], "hash": "14d7feb5203fee810a4ad117d1277607", "device_type": "light"}, "resources": ["color_temp.csv.gz", "model.json", "hs.csv.gz"]}
+129 -5
View File
@@ -155,6 +155,18 @@ def _require_str(value: Any, name: str) -> str:
return value
# Options keys that no code reads any more, stripped by the 3.10 -> 3.11 step and
# again by the one-pass legacy migration. Named once so the two cannot drift.
# The value `DEFAULT_PROFILE_MATCH_MAX_DURATION_RATIO` held before item 311
# widened it. A stored option equal to this is the migration's own seed, not a
# user's choice, and the 3.10 -> 3.11 step heals it.
_OLD_SEEDED_MAX_DURATION_RATIO = 1.5
_DEAD_ABRUPT_KEYS = frozenset(
{"abrupt_drop_ratio", "abrupt_drop_watts", "abrupt_high_load_factor"}
)
async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Migrate config entry to the latest version while preserving settings."""
_log = DeviceLoggerAdapter(_LOGGER, entry.title)
@@ -263,6 +275,45 @@ async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
_healed or "none",
)
# 3.10 -> 3.11: drop the abrupt-drop end-detection knobs from options. The bulk
# strip below reaches only entries still BELOW the current schema, because of the
# early return right under this block - so without a step of its own an entry
# created before 558e71e and since migrated to 3.10 keeps the dead keys forever.
# Same shape as the 3.7 -> 3.8 running_dead_zone retirement, for the same reason.
if version == 3 and minor_version == 10:
# Computed BEFORE the update, like the 3.8 -> 3.9 step: async_update_entry
# replaces entry.options synchronously, so an intersection taken after it
# is always empty and every migration would log "removed nothing".
removed = sorted(set(entry.options) & _DEAD_ABRUPT_KEYS)
new_opts = {k: v for k, v in entry.options.items() if k not in _DEAD_ABRUPT_KEYS}
# Heal the Stage-1 upper gate that item 311 widened 1.5 -> 1.8. The
# legacy migration SEEDS this key with `options.setdefault(...,
# DEFAULT_PROFILE_MATCH_MAX_DURATION_RATIO)`, so every entry migrated
# before that change has a literal 1.5 frozen in its options and the
# manager reads it in preference to the new default - i.e. the widening
# reaches nobody who already had WashData installed. It is not rare:
# 13 of the 33 real exports in `cycle_data/` carry exactly 1.5.
#
# Replaced ONLY where it still equals the old seeded default, exactly as
# the 3.9 -> 3.10 cadence heal is scoped: a user who tuned this produced
# a value like 1.44 or 1.51, not the old constant on the nose. Item 311
# measured the wider gate as 4 appliances better and none worse.
healed: list[str] = []
if new_opts.get(CONF_PROFILE_MATCH_MAX_DURATION_RATIO) == _OLD_SEEDED_MAX_DURATION_RATIO:
new_opts[CONF_PROFILE_MATCH_MAX_DURATION_RATIO] = (
DEFAULT_PROFILE_MATCH_MAX_DURATION_RATIO
)
healed.append(CONF_PROFILE_MATCH_MAX_DURATION_RATIO)
hass.config_entries.async_update_entry(
entry, options=new_opts, minor_version=11
)
minor_version = 11
_log.debug(
"Migrated WashData entry from 3.10 to 3.11 (removed %s; healed %s)",
removed or "nothing",
healed or "nothing",
)
if version == CONFIG_ENTRY_VERSION and minor_version >= CONFIG_ENTRY_MINOR_VERSION:
return True
@@ -326,9 +377,16 @@ async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
options.setdefault(
CONF_PROFILE_MATCH_MIN_DURATION_RATIO, DEFAULT_PROFILE_MATCH_MIN_DURATION_RATIO
)
options.setdefault(
CONF_PROFILE_MATCH_MAX_DURATION_RATIO, DEFAULT_PROFILE_MATCH_MAX_DURATION_RATIO
)
# CONF_PROFILE_MATCH_MAX_DURATION_RATIO is deliberately NOT seeded here.
# Seeding it is what produced `_OLD_SEEDED_MAX_DURATION_RATIO` and the two
# heal sites above: entries took the then-default 1.5 as an explicit value,
# so item 311's widening to 1.8 reached none of them. Every reader resolves
# the key with `DEFAULT_PROFILE_MATCH_MAX_DURATION_RATIO` as its fallback
# and `ws_get_options` ships `defaults` alongside `options`, so an absent
# key behaves identically today and the next change to the default actually
# lands. The min ratio above keeps its seed because that gate is inert for
# ranking (it has never removed a true candidate on the corpus), so freezing
# it costs nothing.
options.setdefault(CONF_MAX_PAST_CYCLES, DEFAULT_MAX_PAST_CYCLES)
options.setdefault(
CONF_MAX_FULL_TRACES_PER_PROFILE, DEFAULT_MAX_FULL_TRACES_PER_PROFILE
@@ -392,6 +450,27 @@ async def async_migrate_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
# so the now-inert "suppress feedback notifications" toggle is stripped.
options.pop("suppress_feedback_notifications", None)
# The abrupt-drop end-detection knobs were removed in 558e71e (the state
# machine reached the same decision from the energy gates), but nothing ever
# stripped them, so they still sit in the options of entries created before
# that. Measured across the real exports in cycle_data/: present in 5 of the
# 7 full user configurations and read by no Python or panel code. The
# surviving "abrupt end" in suggestion_engine is the cycle-artifact
# classifier, which is unrelated hardcoded logic, not these tunables.
# Entries already on the current schema are handled by the 3.10 -> 3.11 step
# above; this covers a one-pass legacy migration.
for k in _DEAD_ABRUPT_KEYS:
options.pop(k, None)
# Same heal as the 3.10 -> 3.11 step, for the cohort that never reaches it:
# an entry below 3.6 (including v1/v2) takes this one-pass path instead of
# the chain, and the `setdefault` above preserves a stored 1.5 rather than
# replacing it. Without this those entries keep the pre-item-311 gate.
if options.get(CONF_PROFILE_MATCH_MAX_DURATION_RATIO) == _OLD_SEEDED_MAX_DURATION_RATIO:
options[CONF_PROFILE_MATCH_MAX_DURATION_RATIO] = (
DEFAULT_PROFILE_MATCH_MAX_DURATION_RATIO
)
# 3.6: coffee_machine / ev / heat_pump / oven device types were removed.
# Remap any entry still on one of them to DEVICE_TYPE_OTHER (Threshold Device),
# preserving all tuned options so no user data is lost.
@@ -1188,6 +1267,11 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
)
manager._logger.info("Applied imported settings to config entry %s", entry_id)
# An old payload re-arms the one-time banked-tail repair. Nothing here
# reloads the entry unless the payload brought settings with it, so
# schedule it directly rather than leaving it for the next restart.
manager.async_schedule_banked_tail_repair()
manager._logger.info("Imported ha_washdata entry %s from %s", entry_id, source)
hass.services.async_register(DOMAIN, "import_config", handle_import_config)
@@ -1320,6 +1404,38 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
hass.services.async_register(DOMAIN, "resume_cycle", handle_resume_cycle)
# Unload confirmation for a device with no door sensor (#451). Deliberately
# not an error when nothing is waiting: an automation wired to a physical
# button fires on every press, and "already emptied" is not a failure.
if not hass.services.has_service(DOMAIN, "mark_unloaded"):
async def handle_mark_unloaded(call: ServiceCall) -> None:
device_id = _require_str(call.data.get("device_id"), "device_id")
registry = dr.async_get(hass)
device = registry.async_get(device_id)
if not device:
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="device_not_found",
)
entry_id = next(
(eid for eid in device.config_entries if eid in hass.data.get(DOMAIN, {})),
None,
)
if not entry_id:
if any(eid for eid in device.config_entries):
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="integration_not_loaded",
)
raise ServiceValidationError(
translation_domain=DOMAIN,
translation_key="no_config_entry",
)
hass.data[DOMAIN][entry_id].mark_unloaded("mark_unloaded service")
hass.services.async_register(DOMAIN, "mark_unloaded", handle_mark_unloaded)
return True
@@ -1418,9 +1534,17 @@ async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
if _cancelled_tasks:
await asyncio.gather(*_cancelled_tasks, return_exceptions=True)
# Release the per-entry write lock so it doesn't block the next setup.
from .ws_api import _WS_WRITE_LOCKS_KEY, async_clear_history_import
# Release the per-entry locks so they don't block the next setup. BOTH of
# them: `_entry_options_lock` is a second per-entry lock created the same
# way, and dropping only the write lock left one asyncio.Lock per removed
# entry in hass.data for the lifetime of the process.
from .ws_api import (
_WS_OPTIONS_LOCKS_KEY,
_WS_WRITE_LOCKS_KEY,
async_clear_history_import,
)
hass.data.get(_WS_WRITE_LOCKS_KEY, {}).pop(entry.entry_id, None)
hass.data.get(_WS_OPTIONS_LOCKS_KEY, {}).pop(entry.entry_id, None)
# Drop any staged history import (uploaded CSV text or a finished scan's
# traces). Nothing else owns that memory, so without this an abandoned upload
+169 -6
View File
@@ -23,6 +23,7 @@ from typing import Any, Optional
import numpy as np
from .const import (
DEFAULT_DTW_BANDWIDTH,
DEFAULT_DTW_MODE,
DEFAULT_PROFILE_MATCH_MAX_DURATION_RATIO,
DEFAULT_PROFILE_MATCH_MIN_DURATION_RATIO,
@@ -65,11 +66,34 @@ def stage4_energy_mode(device_type: str | None) -> str:
return "integrated" if device_type in STAGE4_INTEGRATED_ENERGY_DEVICE_TYPES else "mean"
def _agreement(observed: float, expected: float, scale: float) -> float:
"""1.0 when observed==expected, decaying with the |log-ratio| / scale."""
def _agreement(
observed: float, expected: float, scale: float, gaussian: bool = False
) -> float:
"""1.0 when observed==expected, decaying with the log-ratio / scale.
Two kernels over the same log-ratio. The default is Lorentzian/Cauchy-like
(``1/(1+|x|)``), which decays slowly and so keeps a badly-sized candidate in
contention. ``gaussian`` (``exp(-x^2/2)``) decays fast and separates much
harder on size.
**This is a discrimination choice, not a density fit** (register item 307).
Measured on the corpus, the within-profile duration log-residual is decidedly
NOT normal - excess kurtosis 16.65, |z|>3 at 2.64% against the 0.27% a normal
predicts, and stripping the cycles that cannot match their own profile at all
(item 304) makes it heavier still, not lighter. The Gaussian nonetheless wins
at cycle end because those extreme cycles are unwinnable either way, so the
sharper penalty costs nothing on them and buys separation on the bulk.
The same sharpness is why it must NOT be used mid-cycle: there the observed
duration is a prefix, necessarily far below the profile mean, and a fast kernel
crushes the correct long candidate. The slow tail is what keeps it alive.
"""
if observed <= 0 or expected <= 0 or scale <= 0:
return 0.0
return 1.0 / (1.0 + abs(np.log(observed / expected)) / scale)
ratio = np.log(observed / expected)
if gaussian:
return float(np.exp(-0.5 * (ratio / scale) ** 2))
return 1.0 / (1.0 + abs(ratio) / scale)
_LOGGER = logging.getLogger(__name__)
ALIGNMENT_CONTEXT_BUFFER = 50
@@ -385,7 +409,12 @@ def compute_matches_worker(
min_duration_ratio = config.get("min_duration_ratio", DEFAULT_PROFILE_MATCH_MIN_DURATION_RATIO)
max_duration_ratio = config.get("max_duration_ratio", DEFAULT_PROFILE_MATCH_MAX_DURATION_RATIO)
dtw_bandwidth = config.get("dtw_bandwidth", 0.1)
# Falls back to the OPTION default like every other key here. It used to be a
# literal 0.1, which silently ran a weaker Stage 3 than production for any
# caller that omitted the key - worth ~0.8pp of top-1, and it is what made
# the on-device matching tuner optimise against a pipeline nobody runs
# (register item 309).
dtw_bandwidth = config.get("dtw_bandwidth", DEFAULT_DTW_BANDWIDTH)
dtw_mode = config.get("dtw_mode", DEFAULT_DTW_MODE)
keep_min = float(config.get("keep_min_score", MATCH_KEEP_MIN_SCORE))
corr_weight = float(config.get("corr_weight", MATCH_CORR_WEIGHT))
@@ -547,7 +576,12 @@ def compute_matches_worker(
# rejected (see MATCH_DURATION_SCALE_OVERRUN in const.py).
dur_ag = _agreement(current_duration, prof_dur, dur_overrun_scale)
else:
dur_ag = _agreement(current_duration, prof_dur, dur_scale)
# Gaussian only on a COMPLETED cycle, where the observed duration
# is the real one. Mid-cycle it is a prefix and the sharp kernel
# costs -6.4pp at 60% elapsed (item 307).
dur_ag = _agreement(
current_duration, prof_dur, dur_scale, gaussian=not in_progress
)
sample = cand.get("sample") or []
if in_progress:
cand_mean, cand_span = prefix_mean(
@@ -703,7 +737,11 @@ def prefix_shape_score(
corr_weight = float(config.get("corr_weight", MATCH_CORR_WEIGHT))
score, _metrics, _offset = find_best_alignment(a, b, 1.0, corr_weight=corr_weight)
dtw_bandwidth = float(config.get("dtw_bandwidth", 0.1))
# The SAME default as `compute_matches_worker` (register item 309). Both read
# the same unmutated `config` in one match, so a caller that omits the key
# would otherwise get DEFAULT_DTW_BANDWIDTH for Stage 3 and the old 0.1 here,
# and the two stages would disagree about which candidates look like a prefix.
dtw_bandwidth = float(config.get("dtw_bandwidth", DEFAULT_DTW_BANDWIDTH))
if dtw_bandwidth > 0.0:
dtw_score, _ = _stage3_dtw_score(
a,
@@ -910,6 +948,11 @@ def compute_envelope_worker(
warped onto. Min/max/avg/std bands are still built from all cycles.
Returns:
(time_grid, min_curve, max_curve, avg_curve, std_curve, target_duration) or None.
The bands are the pointwise extremes of the DTW-**warped** members, so
only a consumer that re-derives the same warp
(:func:`align_trace_to_envelope`) can compare an observed trace against
them honestly.
"""
if not raw_cycles_data:
return None
@@ -1122,6 +1165,126 @@ def compute_envelope_worker(
float(target_duration)
)
def align_trace_to_envelope(
t_obs: list[float] | np.ndarray,
p_obs: list[float] | np.ndarray,
time_grid: list[float] | np.ndarray,
reference: list[float] | np.ndarray | None,
dtw_bandwidth: float,
) -> tuple[np.ndarray, bool]:
"""Map each observed sample's time onto an envelope's own time grid.
Uses the **same DTW warp** :func:`compute_envelope_worker` used to build that
envelope's bands, only reduced in the opposite direction (candidate index ->
mean reference index instead of reference index -> mean candidate index).
That matters: ``min``/``max`` are the pointwise extremes of the *warped*
member curves, so a consumer that re-derives the same warp sees every member
inside the band by construction, while a consumer that stretches time
proportionally does not.
A proportional stretch is not a cheap approximation of this, it is a worse
alignment: real programmes absorb their run-to-run duration variance in one
stretch of the cycle (a dishwasher's drying tail), not uniformly, so scaling
the whole axis *moves* the fixed-time features. Measured on the maintainer's
corpus (register item 324) the final heating block's placement error grew
from sd 2.5 min (no scaling at all) to sd 3.7 min under proportional scaling,
which is what fabricated the out-of-band ``spike``/``dip`` artifacts.
Args:
t_obs: observed sample offsets (seconds from cycle start, increasing).
p_obs: observed power values, parallel to ``t_obs``.
time_grid: the envelope's time grid.
reference: the curve to warp onto. Callers pass ``envelope["avg"]``; see
``ProfileStore._align_to_envelope`` for why that beats the build's
own pre-DTW reference. ``None`` forces the proportional stretch.
dtw_bandwidth: Sakoe-Chiba band ratio, same value the build used.
**Known limit, measured and accepted (register item 347).** The observed span
here is ``t_obs[-1]``, while ``_rebuild_envelope_sync`` builds each member
over ``manual_duration or max(last_offset, stored_duration)``. Where the
stored duration runs past the last sample the build covered a slightly longer
span than this re-derivation does, so the warp is not bit-for-bit the build's
own. Measured over the 703 stored cycles in the maintainer's corpus it
affects **7 of them (1.0%)**, median gap 8.2 s and worst 201 s on a 14040 s
cycle (1.4%) - all well inside one grid step. Closing it means threading the
authoritative duration through ``compute_envelope_conformance`` and
``detect_cycle_artifacts``, whose signatures take points and no cycle, and
through their manager / ws_api / playground callers. Not done: that is a wide
change to alignment code whose current behaviour is measured (item 324), for
a sub-grid-step effect on 1% of cycles.
Returns:
``(envelope-space time per observed sample, used_dtw)``. ``used_dtw`` is
False when the warp was unavailable and the proportional stretch was
used instead, so callers can loosen any judgement they base on it.
Never raises: every failure degrades to the proportional stretch.
"""
t_arr = np.asarray(t_obs, dtype=float)
p_arr = np.asarray(p_obs, dtype=float)
tg = np.asarray(time_grid, dtype=float)
def _proportional() -> np.ndarray:
if tg.size < 2 or t_arr.size < 1:
return t_arr
obs_dur = float(t_arr[-1])
env_dur = float(tg[-1])
if obs_dur <= 0 or env_dur <= 0:
return np.clip(t_arr, tg[0], tg[-1])
return np.clip(t_arr * (env_dur / obs_dur), tg[0], tg[-1])
try:
ref = np.asarray(reference, dtype=float) if reference is not None else None
if (
ref is None
or ref.size != tg.size
or tg.size < 2
or t_arr.size < 2
or t_arr.size != p_arr.size
or dtw_bandwidth <= 0
):
return _proportional(), False
obs_dur = float(t_arr[-1])
env_dur = float(tg[-1])
if not (obs_dur > 0 and env_dur > 0):
return _proportional(), False
# Rebuild the per-cycle grid the same way the envelope build did: one
# point per grid step, so an index offset is a time offset on both axes
# and the Sakoe-Chiba band means the same span of minutes either side.
grid_dt = env_dur / (tg.size - 1)
if grid_dt <= 0:
return _proportional(), False
n_obs = int(min(MAX_ALIGN_GRID_POINTS, max(10, int(obs_dur / grid_dt))))
obs_grid = np.linspace(0.0, obs_dur, n_obs)
obs_array = np.interp(obs_grid, t_arr, p_arr)
path = compute_dtw_path(obs_array, ref, band_width_ratio=dtw_bandwidth)
if not path:
return _proportional(), False
path_arr = np.array(path)
obs_indices = path_arr[:, 0]
ref_indices = path_arr[:, 1]
# Average the reference indices a single observed index maps onto (DTW
# paths repeat indices wherever one axis is stretched).
unique_obs, inverse = np.unique(obs_indices, return_inverse=True)
mean_ref = np.zeros_like(unique_obs, dtype=float)
np.add.at(mean_ref, inverse, ref_indices)
mean_ref /= np.bincount(inverse)
env_idx = np.interp(
np.arange(n_obs), unique_obs, mean_ref, left=0, right=tg.size - 1
)
env_time_on_grid = env_idx * grid_dt
mapped = np.interp(t_arr, obs_grid, env_time_on_grid)
return np.clip(mapped, tg[0], tg[-1]), True
except Exception: # pylint: disable=broad-exception-caught
# Alignment is a comparison aid, never a correctness gate: degrade.
return _proportional(), False
def verify_profile_alignment_worker(
current_power: list[float],
envelope_avg_curve: list[float],
+51 -1
View File
@@ -45,6 +45,7 @@ async def async_setup_entry(
WashDataResumeCycleButton(manager, entry),
WashDataRecordStartButton(manager, entry),
WashDataRecordStopButton(manager, entry),
WashDataMarkUnloadedButton(manager, entry),
])
@@ -246,4 +247,53 @@ class WashDataRecordStopButton(ButtonEntity):
async def async_press(self) -> None:
"""Handle the button press."""
await self._manager.async_stop_recording()
await self._manager.async_stop_recording()
class WashDataMarkUnloadedButton(ButtonEntity):
"""Button to confirm the load has been taken out (#451).
The automation-facing half of unload confirmation: a physical button, an NFC
tag or any other automation can press this instead of the door sensor WashData
would otherwise need. Available only while the machine is in the Clean state
(cycle finished, load still inside) - there is nothing to confirm otherwise,
and Home Assistant skips an unavailable entity in a service call, so an
automation that presses it unconditionally is a harmless no-op.
"""
_attr_has_entity_name = True
_attr_translation_key = "mark_unloaded"
_attr_icon = "mdi:basket-check-outline"
def __init__(self, manager: WashDataManager, entry: ConfigEntry) -> None:
"""Initialize the button."""
self._manager = manager
self._entry = entry
self._attr_unique_id = f"{entry.entry_id}_mark_unloaded"
self._attr_device_info = {
"identifiers": {(DOMAIN, entry.entry_id)},
"name": entry.title,
"manufacturer": "WashData",
}
async def async_added_to_hass(self) -> None:
"""Register callbacks."""
self.async_on_remove(
async_dispatcher_connect(
self.hass,
SIGNAL_WASHER_UPDATE.format(self._entry.entry_id),
self._update_callback,
)
)
@callback
def _update_callback(self) -> None:
self.async_write_ha_state()
@property
def available(self) -> bool:
"""Only available while the finished load is still waiting to be taken out."""
return self._manager.is_clean_state
async def async_press(self) -> None:
"""Handle the button press."""
self._manager.mark_unloaded("mark unloaded button")
+222 -5
View File
@@ -155,6 +155,10 @@ NOTIFY_EVENT_TIMER = "cycle_timer" # User-configured mid-cycle countdown timer
CONF_NOTIFY_TITLE = "notify_title"
CONF_NOTIFY_ICON = "notify_icon"
# Per-device accent colour (#454). One setting, three companion-app keys: `color`
# (Android notification accent), `notification_icon_color` (iOS icon glyph tint) and
# `progress_bar_color` (iOS Live Activity bar). Mobile-only, blank = platform default.
CONF_NOTIFY_ICON_COLOR = "notify_icon_color"
CONF_NOTIFY_START_MESSAGE = "notify_start_message"
CONF_NOTIFY_FINISH_MESSAGE = "notify_finish_message"
CONF_NOTIFY_PRE_COMPLETE_MESSAGE = "notify_pre_complete_message"
@@ -225,6 +229,47 @@ CONF_NOTIFY_UNLOAD_MESSAGE = "notify_unload_message" # Template for the clean-l
# taps the notification's "stop reminding" action (opt-in, #374).
CONF_NOTIFY_UNLOAD_REPEAT = "notify_unload_repeat"
# Unload confirmation without a door sensor (#451). Not every machine can have a
# contact sensor on its door (rented flat, steel door, no approval), and the Clean
# state - plus the unload reminder that hangs off it - was reachable only through
# one. Two additions give the same "the load has been taken out" signal:
# - an entity whose activation means unloaded (a Zigbee button as event.* /
# sensor.*, an input_button helper, a motion sensor, a scene), and
# - a plain opt-in for the Mark Unloaded button / ha_washdata.mark_unloaded
# service, for a setup that confirms from its own automation.
# Either one on its own also enables the Clean state on a device with no door
# sensor; with neither set, behaviour is exactly as before.
CONF_UNLOAD_CONFIRM_ENTITY = "unload_confirm_entity"
CONF_UNLOAD_TRACK_WITHOUT_DOOR = "unload_track_without_door"
DEFAULT_UNLOAD_TRACK_WITHOUT_DOOR = False
# How long after the confirmation entity is first seen - or comes back from
# unavailable/unknown - a transition out of `unknown` is still treated as a replay
# rather than a press (register items 367, 368).
#
# A fresh `event.*` / `button.*` / `input_button.*` sits at `unknown` until it is
# first pressed, so excluding `unknown -> value` outright swallowed the first ever
# press. Accepting it outright is not safe either: a z2m action sensor publishes
# its action as a RETAINED MQTT message, which the broker replays on reconnect,
# and that arrives as exactly the same transition. A restart is already covered by
# the separate `old_state is None` guard, so this window only has to cover the gap
# between our subscription and a late-arriving retained value.
#
# Measured against the reference point rather than guessed: the replay lands within
# seconds of the MQTT connection, and the only cost of the window is a genuine press
# in the first two minutes after the entity appears - which is nearly always
# harmless, because `mark_unloaded` is a no-op unless a Clean state is waiting.
#
# **The residual is real and deliberate.** An entity that sits at `unknown` for
# longer than this window and only then receives its first retained value is
# indistinguishable from a first press: a generic HA state change carries no
# "this was retained" marker, and the whole point of the option is that it accepts
# any entity the user already owns (event, button, input_button, binary_sensor,
# sensor, switch, scene, tag), most of which are not MQTT and have no reconnection
# signal to consult. The guards narrow it to that one shape; the cost of being
# wrong is a cleared unload reminder, never lost data.
UNLOAD_CONFIRM_REPLAY_GRACE_S = 120.0
# Quiet hours (do-not-disturb window). Both hours 0-23; unset/None (or start==end)
# = feature off. When configured, finish-type notifications (finish, clean-laundry
# nag, pre-complete/reminder, milestone) that would fire inside the window are held
@@ -325,7 +370,16 @@ DEFAULT_PROFILE_MATCH_MIN_DURATION_RATIO = 0.10 # Allow match after 10% of expe
# harness in devtools/dtw_ab_eval.py: widening 1.3->1.5 lifts commit-recall
# 71.6%->73.4% for a negligible false-positive change; 1.3 was rejecting normal
# longer-than-average runs (extended/anti-wrinkle variants).
DEFAULT_PROFILE_MATCH_MAX_DURATION_RATIO = 1.5
# Stage-1 upper duration gate (register item 311). Raised 1.5 -> 1.8 because at
# 1.5 the gate deleted the TRUE candidate on 14 of 606 corpus folds (2.3%) - a
# cycle that legitimately overran its programme was refused the chance to match
# it at all. Measured against the item-307 kernel, which already penalises
# far-off durations smoothly so the hard gate has less to do: +0.66pp top-1,
# 4 devices better and 0 worse, cluster bootstrap [+0.15,+1.32] P(delta<=0)=0.017.
# Costs ~8% more candidates to score (4.10 -> 4.44 per match, 37 -> 38 ms).
# 2.5 measures slightly higher (+0.83pp) but regresses one device; 1.8 is the
# point at which nothing gets worse.
DEFAULT_PROFILE_MATCH_MAX_DURATION_RATIO = 1.8
DEFAULT_MAX_PAST_CYCLES = 200
DEFAULT_MAX_FULL_TRACES_PER_PROFILE = 20
DEFAULT_MAX_FULL_TRACES_UNLABELED = 20
@@ -441,7 +495,25 @@ ENDING_HARD_FINALIZE_MIN_QUIET_S = 600.0 # continuous sub-threshold span floor
# plausible jitter ratio for a regularly-reporting sensor (whose median equals
# its p95, so the cap never binds and slow meters keep their wide gates) while
# still rejecting the isolated multi-minute holes that caused both reports.
GATE_CADENCE_MEDIAN_FACTOR = 5.0
# Caps the pause/end gate cadence at this multiple of the MEDIAN interval
# (items 213/215). Was 5.0; lowered to 2.0 on measurement (register item 300).
#
# The cap only ever binds when p95 >> median - a fast plug that went quiet -
# which is exactly the case it was introduced for. A uniformly slow meter has
# p95 ~= median, so its gate is set by p95 and this value is irrelevant to it:
# a 300 s-cadence meter keeps its 900 s gate at 2.0 exactly as at 5.0, so the
# original rationale is preserved intact. At 5.0 it was under-correcting the
# case it existed for: on #424's dishwasher (median 38.5 s, p95 970 s) it still
# yielded a 578 s pause gate, which was the single largest remaining component
# of that cycle's 17.9 min late finish.
#
# Measured over 246 clean cycles from the whole corpus at 5.0 vs 2.0: 16 cycles
# that never closed within their stored trace now close, ZERO went the other
# way, and exactly one stored duration changed - 302 s shorter, away from the
# un-evidenced expected-duration fallback it had been pinned to. #424 finishes
# 17.9 -> 10.8 min after the appliance; #427 is unaffected (its gate never
# reached the cap).
GATE_CADENCE_MEDIAN_FACTOR = 2.0
# NOTE: STANDBY_BAND_* constants live further down, after the DEVICE_TYPE_*
# definitions they reference (search "Standby-band stuck-in-RUNNING finalize").
@@ -598,7 +670,51 @@ MATCH_DTW_ENSEMBLE_W = 0.7 # weight on L1 vs DDTW in "ensemble" mode
# in a single np.full, which OOM-kills Home Assistant (issue #388).
MAX_ALIGN_GRID_POINTS = 2000
# Ambiguity: top1-top2 score gap below this flags the match as ambiguous.
# Once a matched cycle has run past its programme's own expected length by this
# ratio, the fallback end gate stops waiting out the full `min_off_gap` and waits
# only END_GATE_LATE_SECONDS of quiet (register item 306). `min_off_gap` exists to
# bridge mid-cycle soak periods; past the end of the programme there is nothing
# left to bridge, and its default is a blind per-device prior (480 s washer /
# 600 s washer-dryer / 3600 s dishwasher) that almost nobody tunes - 12 of the 27
# real user exports carry an effective end wait of 30-60 min because of it.
# Shorten-only and bounded: the user's explicit `off_delay` stays the floor, so
# only the blind prior shrinks, and it is inert on unmatched cycles.
END_GATE_LATE_RATIO = 1.05
END_GATE_LATE_SECONDS = 300.0
# Device-resolved on a washing machine: see END_GATE_LATE_RATIO_BY_DEVICE
# and resolve_end_gate_late_ratio below, next to the device-type constants.
MATCH_AMBIGUITY_MARGIN = 0.05
# Separate, WIDER margin required before a finished cycle is auto-labelled
# (register item 310). Deliberately NOT the same constant as
# MATCH_AMBIGUITY_MARGIN: that one also gates Smart Termination via the
# detector's `_match_ambiguous`, so widening it there would defer terminations
# and undo the end-lag work of item 306. This one is consulted only where a
# label is recorded.
#
# Auto-labelling is the asymmetric decision - a wrong label silently reshapes the
# profile's avg_duration and therefore every future estimate, while a missed one
# only asks the user. Measured over 606 completed folds (post-item-307):
# margin coverage precision wrong labels
# 0.05 84.3% 86.3% 69 <- confidence-only, the old behaviour
# 0.08 77.3% 88.7% 52
# 0.10 72.2% 90.0% 43
# 0.08 is the argmax of right - 2 x wrong (a wrong label costing twice a missed
# one) and was independently selected by grouped cross-validation in all five
# held-out device groups. The absolute score is a much weaker guide here:
# AUC 0.625 against the margin's 0.792.
MATCH_LABEL_MIN_MARGIN = 0.08
# Gap between the best and second-best candidate at which a mid-cycle switch may
# skip the persistence wait (register item 305). The absolute score is close to
# useless for this mid-run (AUC 0.535, because a prefix of a long programme looks
# like a finished short one); the top1-top2 margin reaches AUC 0.773. Replayed over
# 594 cycles x 10 checkpoints: 70.4% -> 72.6% end-of-cycle correctness, 16 better /
# 3 worse, McNemar p = 0.0044, at 0.14 displayed switches per cycle against 0.07.
# The sweep is monotone (0.05 -> +6.7pp at 0.27 flips, 0.08 -> +5.1, 0.10 -> +3.0),
# so this is the conservative end of an accuracy/stability trade. Do not retune it
# in isolation: any Stage-2 scoring change rescales the margin along with it.
MATCH_DECISIVE_MARGIN = 0.12
# Smart Termination landscape guard: when a non-winning candidate is at least this
# much longer than the matched profile AND has a decent shape score (before Stage-4
# duration penalty), the current trace may be a *prefix* of that longer program
@@ -827,7 +943,43 @@ STANDBY_BAND_FINALIZE_DEVICE_TYPES = (
DEVICE_TYPE_WASHER_DRYER,
DEVICE_TYPE_DRYER,
)
STANDBY_BAND_MIN_RATIO = 2.0 # only past 2x the expected duration
# Only past the programme's own expected duration (#445). Was 2.0, which on the
# reporter's 91 min Miele meant 91.5 minutes of idle before the cycle closed:
# that machine sits at 3.2-3.5 W after a programme ends, above its 2.56 W stop
# threshold, so _time_below_threshold never accumulates and this is the ONLY
# path that can end the cycle at all. Measured over 111 clean cycles from the
# whole corpus at 2.0 vs 1.0: no cycle that finished under 2.0 finished any
# differently, and two that never closed within their stored trace now close at
# 103% and 94% of expected. The three plateau conditions below are what make it
# safe - past expected AND >=10 min flat AND <=10% of the cycle's own peak is
# an appliance that has finished, not one still working.
STANDBY_BAND_MIN_RATIO = 1.0 # only past the expected duration
# Ceiling on the measured post-activity quiet span a stored cycle may bank
# (register item 297). `profile_terminal_quiet_seconds` is a median over that profile's own
# cycles, so it is already self-limiting; this is the guard against a corrupted
# or hand-edited value licensing an unbounded tail - the one thing the field
# exists to prevent. 30 min comfortably covers a dishwasher's passive drying
# phase, measured at a median 11% of the cycle and reaching 43%.
TERMINAL_QUIET_CAP_S = 1800.0
# A measured quiet span is only trusted as a tail allowance when the profile has
# actually shown it repeatedly (register item 297). Measured over 20 real profiles: the two
# dishwashers, which genuinely end in a passive drying phase, scored 20/20 and
# 17/17; every washing-machine profile that produced a value at all did so from
# 1-4 cycles out of 4-12, one of them 2400 s. Below these floors the accessor
# reports None and the caller keeps its previous behaviour.
TERMINAL_QUIET_MIN_OBSERVATIONS = 3
TERMINAL_QUIET_MIN_CONSISTENCY = 0.6
# Store key set by the v12->v13 migration and cleared once the one-time repair
# of banked cycle tails has run (register item 297). The repair itself cannot
# live in the storage migration, which sees only the store payload: deciding
# where a cycle's real activity ended needs `stop_threshold_w`, and that lives
# in entry.options. Same split as the v10->v11 marker-only bump.
BANKED_TAIL_REPAIR_KEY = "_banked_tail_repair_pending"
# Don't churn a cycle for a few seconds of tail: only rewrite one whose banked
# span is worth correcting. Measured median banking was 12.6 min, so this only
# skips noise.
BANKED_TAIL_REPAIR_MIN_S = 60.0
STANDBY_BAND_WINDOW_S = 600.0 # require a >=10 min flat plateau
STANDBY_BAND_MAX_FRACTION = 0.10 # plateau level <= 10% of the cycle's peak
STANDBY_BAND_FLATNESS_FRACTION = 0.03 # window (max-min) <= 3% of the cycle's peak
@@ -1156,6 +1308,33 @@ def resolve_watchdog_interval_default(device_type: str) -> int:
return int(max(DEFAULT_WATCHDOG_INTERVAL, 2.0 * sampling + 1.0))
def resolve_min_off_gap_default(device_type: str) -> int:
"""Device-resolved minimum off gap (#445).
Published to the panel so the number that actually governs the end of a cycle
is visible. ``CycleDetector`` waits
``effective_off_delay = max(off_delay, min_off_gap)``, so an unset
``min_off_gap`` silently raises a hand-lowered ``off_delay`` to this blind
per-device prior - 480 s on a washing machine, 3600 s on a dishwasher. The
#445 reporter set ``off_delay`` to 180 s, waited 6 minutes, and force-stopped
three cycles because nothing told them the real wait was 480 s.
Deliberately published rather than lowered. The prior is there because a long
quiet stretch inside a cycle must not split it in two, and that is real: a
corpus sweep over 15 devices' stored cycles found dishwasher quiet-and-resumed
stretches up to **6791 s**. Only 2 of those 15 devices left ``min_off_gap``
unset at all, so honouring the lowered ``off_delay`` would be a no-op almost
everywhere and a cycle-splitting risk on exactly the device class that needs
the bridge. Showing the number lets the user make that call per appliance.
"""
return int(DEFAULT_MIN_OFF_GAP_BY_DEVICE.get(device_type, DEFAULT_MIN_OFF_GAP))
def resolve_off_delay_default(device_type: str) -> int:
"""Device-resolved off delay (#445), published for the same reason."""
return int(DEFAULT_OFF_DELAY_BY_DEVICE.get(device_type, DEFAULT_OFF_DELAY))
def resolve_start_duration_default(device_type: str) -> float:
"""Device-resolved start-debounce default (#396).
@@ -1183,6 +1362,40 @@ DEFAULT_SMART_TERMINATION_DURATION_RATIO_BY_DEVICE = {
}
# ...except on a washing machine, where 1.05 is structurally out of reach
# (register item 355). A washer's programme length is load-adaptive, so a run is
# BELOW its profile's mean duration about half the time by definition: measured
# over the `end_gate_eval.py` corpus the median washer reaches only 0.83 of the
# bar before it stops, against 1.01 for a dishwasher, and the shortening fired on
# 6.8% of washer cycles. That is what left washing machines waiting out the full
# `min_off_gap` - a median 24.74 min after the last activity, against 6.89 for a
# dishwasher. NOT an ambiguity problem, which is what the register used to say;
# item 330 had already fixed that half.
#
# Scoped to the device class rather than lowered globally, because every early
# end in the global sweep was a DISHWASHER: at 0.95 one dishwasher closed 5.58
# min before its last activity (a 30 s 73 W blip after five dead-zero minutes),
# while washing machines showed 0.00% early ends at every ratio tried. Measured
# at 0.90, washers only: median end lag 24.74 -> 16.92 min, early ends 0.00%,
# splits unchanged at 1.90%, dishwashers byte-identical. The cost is 2 cycles of
# 158 losing their auto-label (92.4% -> 91.1%) because ending ~11 min sooner can
# skip a final match tick; they are still stored and offered for confirmation,
# which this codebase already treats as much cheaper than a wrong label (#325).
END_GATE_LATE_RATIO_BY_DEVICE = {
DEVICE_TYPE_WASHING_MACHINE: 0.90,
DEVICE_TYPE_WASHER_DRYER: 0.90,
}
def resolve_end_gate_late_ratio(device_type: str) -> float:
"""Device-resolved bar for the item-306 ENDING shortening.
Single source of truth for the detector and the Playground replay, so the
sim cannot diverge from live the way it did in item 352.
"""
return END_GATE_LATE_RATIO_BY_DEVICE.get(device_type, END_GATE_LATE_RATIO)
def resolve_smart_termination_duration_ratio_default(device_type: str) -> float:
"""Device-resolved Smart-Termination duration ratio default (#393).
@@ -1218,6 +1431,10 @@ TERMINAL_EVENT_PEAK_FRAC = 0.004
TERMINAL_QUIET_MIN_S = 120.0
# Below this many evidence cycles the medians describe noise, not the programme.
TERMINAL_SIGNATURE_MIN_CYCLES = 3
# Cycles a profile needs before one of them can be called a duration outlier
# (register item 304). Below this there is no established 'usual length' to be
# an outlier from, and calling a program's second cycle an error is nonsense.
SELF_UNMATCHABLE_MIN_CYCLES = 3
# Storage
# v6: backfill ml_review.golden=True for manually-recorded cycles (recorded ==
@@ -1240,7 +1457,7 @@ TERMINAL_SIGNATURE_MIN_CYCLES = 3
# training labels and the feedback queue, and is retention-evicted oldest-first) nor
# `reference_cycles` (curated community-store templates, golden by construction).
# Additive `setdefault`, so it is idempotent and loses nothing.
STORAGE_VERSION = 12
STORAGE_VERSION = 13
STORAGE_KEY = "ha_washdata"
# ─── Config-entry schema version (NOT the storage version above) ───────────────
@@ -1250,7 +1467,7 @@ STORAGE_KEY = "ha_washdata"
# leaves an entry a version short, which then re-migrates on every start - and repeating
# the literals in three places is what made that easy to do.
CONFIG_ENTRY_VERSION = 3
CONFIG_ENTRY_MINOR_VERSION = 10
CONFIG_ENTRY_MINOR_VERSION = 11
# Notification events
EVENT_CYCLE_STARTED = "ha_washdata_cycle_started"
+677 -51
View File
@@ -66,8 +66,13 @@ from .const import (
ENDING_HARD_FINALIZE_RATIO,
ENDING_HARD_FINALIZE_MIN_QUIET_S,
GATE_CADENCE_MEDIAN_FACTOR,
END_GATE_LATE_RATIO,
resolve_end_gate_late_ratio,
END_GATE_LATE_SECONDS,
STANDBY_BAND_FINALIZE_DEVICE_TYPES,
STANDBY_BAND_MIN_RATIO,
DEVICE_TYPE_DISHWASHER,
TERMINAL_QUIET_CAP_S,
STANDBY_BAND_WINDOW_S,
STANDBY_BAND_MAX_FRACTION,
STANDBY_BAND_FLATNESS_FRACTION,
@@ -112,7 +117,11 @@ ML_END_GUARD_MAX_DEFER_SECONDS = 1800.0 # cap the extra wait the guard may add
ML_PROVIDER_THROTTLE_SECONDS = 30.0
if not 0 < DISHWASHER_END_SPIKE_MIN_PROGRESS < 1:
raise ValueError("DISHWASHER_END_SPIKE_MIN_PROGRESS must be a fraction in (0, 1)")
from .signal_processing import energy_gap_threshold_s, integrate_wh
from .signal_processing import (
energy_gap_threshold_s,
integrate_wh,
quiet_run_before,
)
_LOGGER = logging.getLogger(__name__)
@@ -291,6 +300,61 @@ def trim_zero_readings(
return readings[start_idx : end_idx + 1]
def terminal_high_for_guards(
store: Any,
config: "CycleDetectorConfig",
cycle_max_power: Any,
profile_name: str | None,
) -> tuple[float, ...] | None:
"""Element 10 of the match tuple: the matched profile's last high-power block.
Two consumers with two different bars, and the bar has to travel with the
block (register item 351):
* **anti-crease** (#399) measures against ``anti_wrinkle_max_power``, the
dryer's "a tumble is below this" level. Only meaningful while anti-wrinkle
is on, and it returns a TRIPLE.
* **the standby-band finalise** (#296 / #445) shares the same predicate and
used to get nothing at all, because element 10 was supplied only when
anti-wrinkle was enabled and ``DEFAULT_ANTI_WRINKLE_ENABLED`` is False. So
on a washing machine ``_anticrease_spin_pending`` returned False at once
and the machine could finalise on the quiet plateau before its final spin,
recording that spin as a second cycle. The bar here is a share of the
cycle's own peak, and it is returned as a QUAD so
``_high_power_seconds_since`` counts live seconds against the same number.
**This lives here, module level, because it had two copies.** The manager
builds the live match tuple and ``playground`` builds the sim's, and
``end_gate_eval.py`` drives the detector through the Playground - so an arm
present in only one of them is invisible to every measurement made with that
harness, which is exactly how item 352 first measured as a no-op. The copies
had already drifted in their error handling before they were merged.
Total by construction: every failure path returns None, which leaves the
guard exactly as inert as it was. That is the fail-open direction every input
here takes, and it is also what lets ``playground`` call it directly while
keeping its own never-raise contract.
"""
if not profile_name or store is None:
return None
try:
if config.anti_wrinkle_enabled:
return store.profile_terminal_high_block(
profile_name, config.anti_wrinkle_max_power
)
if config.device_type not in STANDBY_BAND_FINALIZE_DEVICE_TYPES:
return None
ceiling = float(cycle_max_power or 0.0) * STANDBY_BAND_MAX_FRACTION
if ceiling <= 0:
return None
block = store.profile_terminal_high_block(profile_name, ceiling)
if block is None:
return None
return (float(block[0]), float(block[1]), float(block[2]), ceiling)
except Exception: # noqa: BLE001 - a guard input must never break matching
return None
class CycleDetector:
"""Detects washing machine cycles based on power usage.
@@ -403,6 +467,13 @@ class CycleDetector:
self._last_match_time: datetime | None = None
self._expected_duration: float = 0.0
self._last_match_confidence: float = 0.0
# Element 12: the longest expected duration among the candidates the
# matcher still considers plausible. `_match_prefix_ambiguous` means one
# of them is materially longer than the winner, so "past the expected
# end" might be "mid-soak in that longer programme" - but only up to
# THIS duration. Past it there is no longer programme left to be mid-soak
# in, and the guard's own rationale is spent.
self._longest_candidate_duration: float = 0.0
self._end_spike_seen: bool = False
self._end_spike_duration: float = 0.0 # cycle duration (s) when _end_spike_seen was last set
self._match_ambiguous: bool = False # last live match was ambiguous (gates predictive end)
@@ -423,6 +494,15 @@ class CycleDetector:
self._matched_terminal_high: (
tuple[float, float] | tuple[float, float, float] | None
) = None
# Element 11 (register item 297): how long the matched profile is MEASURED to stay quiet
# after its last real activity. Bounds how much of Smart Termination's
# confirmation delay may be banked into the stored duration. None means the
# profile has not been measured, and for a DISHWASHER the previous
# expected-end cap applies. Only a dishwasher gets that far: every other
# device type returns `_last_active_time` from `_keep_tail_cap` before
# this element is read at all, because nothing legitimate follows their
# last activity - so for them the cap can sit EARLIER than expected_end.
self._matched_terminal_quiet_s: float | None = None
# One-shot per cycle, so the held-finalise reason is visible in the log
# without repeating it on every reading.
self._anticrease_spin_wait_logged: bool = False
@@ -493,6 +573,15 @@ class CycleDetector:
cannot triple the gate. Only these two gates read it - ``_p95_dt`` itself
is left alone so every outage ceiling keeps the cadence snapshot it was
tuned against (register items 213, 215).
The cap alone was not enough, and the reason is worth keeping: it is
computed over the same 20-interval window it is meant to protect. Once a
publish-on-change plug falls silent the watchdog's own 0 W keepalives are
the only readings left, so the median collapses onto the injection spacing
too and ``5 x median`` stops binding - the gate then grows with our
injection rate instead of the plug's. Fixed at the source (register item
289): ``process_reading`` no longer trains the cadence on synthetic
readings, so this window describes the sensor and nothing else.
"""
if len(self._recent_dts) < 5:
return self._p95_dt
@@ -639,6 +728,47 @@ class CycleDetector:
return self._SANITIZE_INVALID_SENTINEL
return value
@staticmethod
def _sanitize_terminal_quiet(raw: Any) -> float | None:
"""Coerce a measured post-activity quiet span into a finite, non-negative
float, else None (register item 297).
Same discipline as the two siblings: None means "no opinion", and for a
DISHWASHER ``_keep_tail_cap`` then behaves exactly as it did before this
element existed. Other device types never reach that branch - they are
capped at ``_last_active_time`` higher up - so None changes nothing for
them either way. Bounded above by ``TERMINAL_QUIET_CAP_S`` so a corrupted
or hand-edited value cannot license an unbounded tail - the one thing
this field exists to prevent.
"""
if raw is None:
return None
try:
value = float(raw)
except (TypeError, ValueError, OverflowError):
return None
if not math.isfinite(value) or value < 0:
return None
return min(value, TERMINAL_QUIET_CAP_S)
@staticmethod
def _sanitize_longest_candidate(raw: Any) -> float:
"""Coerce the longest plausible candidate duration to a usable bound.
Unlike the three siblings above, "no opinion" is ``0.0`` rather than
``None``: the ENDING gate reads a non-positive bound as "no information"
and keeps the old refusal, which is the safe direction. Not routed
through ``_sanitize_expected_duration`` because 0.0 is legitimate here
and that helper logs it as invalid.
"""
try:
value = float(raw or 0.0)
except (TypeError, ValueError, OverflowError):
return 0.0
if not math.isfinite(value):
return 0.0
return value if 0.0 < value <= CycleDetector._SANITIZE_MAX_EXPECTED_DURATION else 0.0
@staticmethod
def _sanitize_tail_power(raw: Any) -> float | None:
"""Coerce ``raw`` into a finite, positive float, else None (#364).
@@ -668,9 +798,21 @@ class CycleDetector:
@staticmethod
def _sanitize_terminal_high(
raw: Any,
) -> tuple[float, float] | tuple[float, float, float] | None:
"""Coerce ``raw`` into a ``(start_frac, seconds)`` pair or a
``(start_frac, seconds, start_offset_s)`` triple, else None (#399).
) -> (
tuple[float, float]
| tuple[float, float, float]
| tuple[float, float, float, float]
| None
):
"""Coerce ``raw`` into a ``(start_frac, seconds)`` pair, a
``(start_frac, seconds, start_offset_s)`` triple, or a
``(start_frac, seconds, start_offset_s, ceiling_w)`` quad, else None (#399).
The fourth element is the watts the block was MEASURED against (register
item 351). Anti-crease measures against ``anti_wrinkle_max_power`` and
sends a triple; the standby-band path measures against a share of the
cycle's own peak and must say so, because the live counter has to count
seconds above the same bar or the two halves compare different things.
None means "no opinion", which leaves ``_anticrease_spin_pending`` inert and
the anti-crease finalise exactly as it behaved before the guard existed.
@@ -695,7 +837,7 @@ class CycleDetector:
values = list(raw)
except TypeError:
return None
if len(values) not in (2, 3):
if len(values) not in (2, 3, 4):
return None
try:
start_frac = float(values[0])
@@ -716,7 +858,19 @@ class CycleDetector:
return (start_frac, seconds)
if not math.isfinite(start_offset) or start_offset < 0:
return (start_frac, seconds)
return (start_frac, seconds, start_offset)
if len(values) == 3:
return (start_frac, seconds, start_offset)
try:
ceiling = float(values[3])
except (TypeError, ValueError, OverflowError):
return (start_frac, seconds, start_offset)
# A non-positive or non-finite ceiling degrades to the triple rather than
# to None: the triple still arms the guard against
# ``anti_wrinkle_max_power``, and this method must never be able to
# DISARM a guard that would otherwise arm.
if not math.isfinite(ceiling) or ceiling <= 0:
return (start_frac, seconds, start_offset)
return (start_frac, seconds, start_offset, ceiling)
def _trailing_mean_power(self, timestamp: datetime, window_s: float) -> float | None:
"""Time-weighted mean power over the trailing ``window_s``, or None when
@@ -918,6 +1072,23 @@ class CycleDetector:
self._matched_terminal_high = (
self._sanitize_terminal_high(result_seq[9]) if len(result_seq) >= 10 else None
)
# Element 11 (register item 297): cleared by a shorter tuple for the same reason as
# elements 9 and 10 - a newly matched programme must not inherit the
# previous one's tail.
self._matched_terminal_quiet_s = (
self._sanitize_terminal_quiet(result_seq[10])
if len(result_seq) >= 11
else None
)
# Element 12: longest plausible candidate duration (see the attribute's
# own comment). A shorter tuple clears it, like elements 9-11, so a
# stale value can never license a shortening for a different match.
# Coerced quietly, not through _sanitize_expected_duration: 0.0 is a
# legitimate "no candidate durations to compare" here, and that helper
# logs it as invalid.
self._longest_candidate_duration = self._sanitize_longest_candidate(
result_seq[11] if len(result_seq) >= 12 else 0.0
)
else:
# Assume MatchResult object or similar (future proofing)
# But for now wrapper returns tuple
@@ -931,6 +1102,7 @@ class CycleDetector:
self._match_prefix_ambiguous_full_shape = False
self._matched_tail_power = None
self._matched_terminal_high = None
self._matched_terminal_quiet_s = None
elif match_name:
# If sanitization rejected the expected_duration, treat the match
@@ -989,6 +1161,15 @@ class CycleDetector:
self._match_prefix_ambiguous_full_shape = False
self._matched_tail_power = None
self._matched_terminal_high = None
self._matched_terminal_quiet_s = None
# Element 12 belongs with them: its own comment claims a stale value can
# never license a shortening for a different match, and that was only
# true of the tuple path. Left here across a reset, a small positive
# bound survives into the next cycle, where it is neither greater than
# `_expected_duration` (so the bar is not raised) nor <= 0 (so the "no
# information" refusal does not fire) - the one combination that lets an
# ambiguous match shorten `effective_off_delay` on no evidence.
self._longest_candidate_duration = 0.0
self._anticrease_spin_wait_logged = False
# Per-cycle diagnostic throttle (#346): the "Smart Termination not applied"
# line only logs when the reason CHANGES. Carrying the previous cycle's
@@ -1119,22 +1300,42 @@ class CycleDetector:
return configured_ratio
def process_reading(
self, power: float, timestamp: datetime, synthetic: bool = False
self,
power: float,
timestamp: datetime,
synthetic: bool = False,
observed: bool = True,
) -> None:
"""Process a new power reading using robust dt-aware logic.
``synthetic=True`` marks a reading the *manager* injected rather than one
the power sensor sent: the watchdog and anti-wrinkle keepalives, which
exist to advance the quiet timers while a change-only plug says nothing.
They must keep doing exactly that, so this flag changes no timing here.
It is recorded only so that anything reasoning about what was OBSERVED can
tell the two apart. **Nothing consumes it yet**: it was added for
`_keep_tail_cap` (register item 238) and that use was implemented,
measured and reverted, because after `_last_active_time` every reading is
below the stop threshold anyway, so a plug still reporting cannot separate
a drying phase from standby - it only shows the plug is chatty. Kept
because the distinction is correct and cheap to carry; see item 260 for
the two other consumers that were measured and rejected.
They must keep doing exactly that, so this flag does not change the quiet
accumulators. What it does change is the two places that reason about what
the SENSOR did (#424):
* ``_update_cadence`` is skipped. The cadence estimate feeds
``_gate_cadence`` and therefore the pause/end gates, so training it on
our own injections makes those gates a function of how often we inject -
see the note on ``_gate_cadence``.
* **with ``observed=True``**, the gap-free tally treats the interval as
observed, because the watchdog re-anchored on the sensor's live state
(``_resync_power_from_state``) before injecting - so the keepalive is a
moment we looked rather than a hole in the record.
``observed=False`` says the sensor state could NOT be read when this
keepalive was injected (unavailable / unknown / non-finite). The second
bullet is then false: the interval it closes is a genuine outage, and the
gap-free tally is reset like any other hole - at ANY step size, since the
watchdog injects far more often than the outage ceiling.
(The `_keep_tail_cap` use this flag was originally added for, register
item 238, was implemented, measured and reverted: after
``_last_active_time`` every reading is below the stop threshold anyway, so
a plug still reporting cannot separate a drying phase from standby - it
only shows the plug is chatty. See item 260 for two more that were
measured and rejected.)
"""
if not synthetic:
self._last_real_reading_time = timestamp
@@ -1146,6 +1347,19 @@ class CycleDetector:
# Sanity check for negative dt
if dt < 0:
# Logged because this is the one exit from process_reading that leaves
# NO trace: the accumulators do not advance, `_power_readings` does not
# grow, and every downstream gate therefore stays silent too. A cycle
# wedged behind it looks exactly like a cycle whose end gate is simply
# not satisfied - which is how much of register item 320 was spent.
self._logger.debug(
"Ignoring reading %.1fW: timestamp %s is %.1fs before the last "
"processed reading %s",
power,
timestamp,
-dt,
self._last_process_time,
)
self._last_process_time = timestamp
return
@@ -1193,7 +1407,19 @@ class CycleDetector:
# is supposed to catch it (a 120 s gap after a 10 s cadence lifts p95 to
# ~15.5 s -> ceiling 155 s -> the gap counts as observed quiet).
self._prior_p95_dt = self._p95_dt
self._update_cadence(dt)
# Only the SENSOR trains the cadence estimator (#424). The watchdog's 0 W
# keepalives exist because the plug fell silent, so once it does every
# interval left in `_recent_dts` is one we manufactured: p95 AND the
# median both collapse onto the injection spacing, the `5 x median` cap in
# `_gate_cadence` stops binding, and the pause/end gates - three times that
# cadence - grow with our own injection rate. Measured on the #424
# reporter's v0.5.6 cycle: the gate climbed 192 s -> 530 s on injected
# readings alone, taking the end gate to 1605 s and holding PAUSED for
# 1060 s. Skipping them leaves the estimate describing the plug, which is
# the only thing it is supposed to describe; the accumulators below still
# advance on every reading, synthetic or not.
if not synthetic:
self._update_cadence(dt)
self._last_process_time = timestamp
# 1b. Pre-roll buffer (#430): record every reading seen while no cycle is
@@ -1272,8 +1498,33 @@ class CycleDetector:
# 3600)) but reuses the maintained p95 cadence to stay O(1) in this
# per-reading hot path. Uses the cadence as it stood BEFORE this
# reading, so a gap cannot widen its own acceptance threshold.
# A synthetic keepalive is normally not an outage: the watchdog
# resyncs against the sensor's live state before injecting, so the
# interval it closes IS observed. This matters because the ceiling is
# derived from the p95 cadence, which synthetic readings no longer
# train - without the exemption a 106 s keepalive on a 2 s-cadence
# plug would look like a 106 s hole and reset the tally on every
# tick, starving the two consumers that can only ever SHORTEN the
# wait (the dishwasher end-spike quiet release and the ENDING hard
# finalize).
#
# `observed` is what makes that premise true rather than assumed.
# `_resync_power_from_state` returns early when the sensor is
# unavailable / unknown / non-finite, but the watchdog injects anyway
# - it only checks the silence interval. So during a real telemetry
# outage every keepalive was exempt and the gap-free tally grew
# through quiet nobody ever saw, which is exactly what that tally
# exists not to count. The caller now says whether the sensor state
# could actually be read, and an unread sensor is an outage.
# An unread sensor is an outage HOWEVER SHORT each keepalive step
# is, which is why this is its own clause and not a qualifier on the
# ceiling test. The watchdog injects once per `watchdog_interval`
# (floor 30 s, effective 30-60 s) and the ceiling is at least 60 s,
# so during a real outage every individual `dt` sits under the
# ceiling - qualifying the ceiling test left the tally accumulating
# exactly as before, which is the bug this is meant to fix.
outage_ceiling = min(3600.0, max(60.0, 10.0 * self._prior_p95_dt))
if dt > outage_ceiling:
if (synthetic and not observed) or (dt > outage_ceiling and not synthetic):
self._time_below_threshold_gapfree = 0.0
else:
self._time_below_threshold_gapfree += dt
@@ -1976,9 +2227,9 @@ class CycleDetector:
# real pump-out at ~99% arms the end-spike first. Takes the
# SOONER of the two anchors, so it can only ever shorten the
# wait, never extend it.
_spike_wait = self._dishwasher_end_spike_wait_s()
past_wait_period = current_duration >= (
self._expected_duration
+ DISHWASHER_END_SPIKE_WAIT_SECONDS
self._expected_duration + _spike_wait
) or (
current_duration >= self._expected_duration
and self._time_below_threshold_gapfree
@@ -1994,7 +2245,7 @@ class CycleDetector:
"expected %.0fs + %.0fs wait)",
current_duration,
self._expected_duration,
DISHWASHER_END_SPIKE_WAIT_SECONDS,
_spike_wait,
)
return # Don't finish yet, wait for spike
@@ -2030,7 +2281,7 @@ class CycleDetector:
# resetting the quiet timer) — asymmetric, shorten-only. Not
# for user-paused cycles.
required_quiet = max(
ENDING_HARD_FINALIZE_MIN_QUIET_S,
self._ending_hard_finalize_quiet_s(),
float(max(self._config.off_delay, self._config.min_off_gap)),
)
# Require the required_quiet tail to be actually SAMPLED (no
@@ -2080,6 +2331,166 @@ class CycleDetector:
# Rule: To separate cycles, we must wait at least min_off_gap.
effective_off_delay = max(self._config.off_delay, self._config.min_off_gap)
# Progress-aware shortening (register item 306). `min_off_gap` is
# there to bridge mid-cycle soak periods; once the run is past the
# matched programme's OWN expected length there is no soak left to
# bridge, so continuing to wait out a blind per-device prior just
# reports the end late.
#
# Measured on `devtools/end_gate_eval.py`, which IS committed.
# **`cycle_data/` is not**, so every n below depends on the local
# corpus and two different counts get quoted: cycles REPLAYED,
# and the subset that reached an ENDING exit, which is the only
# one that yields a lag and is what the harness table's `n`
# column reports. Current corpus: 273 replayed, 263 measured.
# Figures attributed below to "211/221" predate the #445 / #427 /
# #424 reporter exports being added mid-PR-#448, when the same
# corpus was 221 replayed / 211 measured. They are the same
# harness over a smaller corpus, not a drift.
#
# Re-cut on the current corpus (`--no-shortening` vs shipped,
# paired over 273 cycles): median end lag 13.43 -> 12.00 min
# overall and 27.50 -> 26.00 for washing machines, dishwasher p90
# 33.50 -> 19.70, and early ends (1.14% / 0.00%) and splits
# (2.66%) **identical in every scope**. It moves 32 of 273
# cycles: it buys little because it reaches few, and it costs
# nothing. Item 306's original figures (427 cycles, washing
# machines 12.9 -> 7.5 min, splits 3.75%) came from a harness
# that was never committed and **do not reproduce**; the safety
# half reproduces exactly. Treat the 427-cycle numbers as
# unverified; register item 329 holds that reconciliation, taken
# on the 211-measured corpus. Re-cut anything new with
# `end_gate_eval.py` rather than a throwaway script.
#
# Asymmetric and bounded, in the same spirit as _keep_tail_cap: it
# can only ever shorten, keeps the user's explicit `off_delay` as
# the floor (only the blind prior shrinks), and is inert when
# nothing matched. The bar it waits for is DEVICE-RESOLVED, not a
# fixed 1.05x (register item 355): `resolve_end_gate_late_ratio`
# returns 0.90 for `washing_machine` / `washer_dryer` and
# `END_GATE_LATE_RATIO` (1.05) for everything else - so on a washer
# the shortening starts BEFORE the expected end. That is the point:
# a washer's programme is load-adaptive, so a run sits below its
# profile mean about half the time by definition and the median
# washer reaches only 0.83 of a 1.05 bar, which put the rule out of
# reach for the device type that needed it most. Early ends stayed
# at 0.00% for washers at every ratio measured; the dishwashers,
# which do produce early ends below 1.0, keep 1.05.
# Gated on the SAME guards Smart Termination respects. The rule
# keys on `_expected_duration`, so it must not fire while the
# matcher says that duration is in doubt: `_match_prefix_ambiguous`
# means a much longer look-alike is still plausible, and then "past
# the expected end" may really be "mid-soak in a longer programme".
# Without this the fallback timeout walks straight through the
# prefix-landscape guard and re-opens the #288 split-cycle bug -
# caught by test_smart_termination_blocked_by_prefix_ambiguous,
# where a 450 s soak dip sits right at the short profile's end.
# NOT also gated on `_last_match_confidence >=
# match_confidence_threshold`, and that is deliberate, not an
# oversight - the paragraph above says "the SAME guards Smart
# Termination respects" and means the two ambiguity flags.
# Smart Termination does check confidence, because it ENDS a cycle
# early on a prediction; this rule only shortens a wait that is
# already past the programme's own expected end, so the asymmetry
# is intended. Adding the check was tried (PR #448 round 6) and
# measured on `devtools/end_gate_eval.py` over 221 replayed real
# cycles (211 of them measurable, the pre-reporter-export corpus
# described above): it moves **2 of them**, delaying one by 10.5
# min and one by 21 min, while early ends (1.42% / 0.00% at the >1
# and >5 min marks) and splits (3.32%) stay **exactly** where they
# were. It
# prevented no split and no early end - pure cost, so it was
# reverted. The corpus carries only 4 matched cycles under 0.4
# confidence, so it cannot prove the guard harmless either; the
# exposure is real (cycle `7c4598310016` is a 3h37m wash matched at
# 0.372 to a profile named "1:07", i.e. running the shortened gate
# for over two hours) and it still did not split. Re-run the
# harness before re-litigating this.
if (
self._matched_profile
and self._expected_duration > 0
and self._current_cycle_start is not None
):
_elapsed = (timestamp - self._current_cycle_start).total_seconds()
# The bar this run has to clear. Normally the matched
# programme's own expected end; while the matcher still thinks
# a materially LONGER programme is plausible, that longer one's
# end instead (register item 330).
#
# Blocking outright on the two ambiguity flags - which is what
# this did until item 330 - was costing almost every cycle the
# shortening. Measured on `devtools/end_gate_eval.py`: of the
# 94 cycles that ever pass 1.05x their own expected duration,
# **84 (89%) were blocked by an ambiguity flag**, so the rule
# reached 4.7% of cycles (10 of the 211 measurable on the
# pre-reporter-export corpus) and the median cycle still
# waited out the full `min_off_gap`.
#
# The flags are not wrong, they are too coarse. Both exist to
# protect `_expected_duration` against "this is really a prefix
# of something longer" (#288 / #364) - a statement about
# DURATION, not about which label wins. Two programmes that
# score within the ambiguity margin and run the same length
# leave "past the expected end" true either way. So instead of
# refusing, raise the bar to the longest duration still in
# play: past THAT, no candidate is left for this to be a
# mid-soak of, which is exactly the condition the guard was
# standing in for.
#
# Absent information keeps the OLD refusal. A caller that does
# not send element 12 (an older Playground, most tests, any
# short tuple) leaves `_longest_candidate_duration` at 0.0,
# and an ambiguous match with no candidate durations to
# compare must block exactly as it did before - otherwise the
# #288 split-cycle reproduction
# `test_smart_termination_blocked_by_prefix_ambiguous` walks
# straight through, which is how the first draft of this was
# caught.
_bar = self._expected_duration
_blocked = False
_bar_raised = False
if self._match_prefix_ambiguous or self._match_ambiguous:
if self._longest_candidate_duration > _bar:
_bar = self._longest_candidate_duration
_bar_raised = True
elif self._longest_candidate_duration <= 0.0:
# No information: a caller that does not send element
# 12 keeps the old refusal (see below). The bound
# itself is now derived from the FULL candidate
# population, the same one `_match_prefix_ambiguity`
# judges, so a longer candidate ranked sixth or lower
# can no longer set the flag while hiding from the
# bar - which it could when this read
# `MatchResult.candidates`, i.e. `candidates[:5]`.
_blocked = True
# Device-resolved (register item 355): 1.05 is out of reach
# for a load-adaptive washer, which reaches a median 0.83 of
# its EXPECTED duration before it stops.
#
# **But never against a RAISED bar.** When the match is
# ambiguous and a longer candidate exists, `_bar` is no longer
# the expected duration - it is the longest plausible
# programme, and it was raised precisely to say "a much longer
# look-alike is still on the table, so past the expected end
# does not mean done". Discounting that by 0.90 would shorten
# the wait 10% BEFORE the candidate it represents could even
# finish, and on the shipped washer defaults that drops the
# wait from `min_off_gap` to `max(off_delay, 300)` - one quiet
# interval away from finalising mid-programme and recording the
# rest as a second cycle, which is #288. The item-355 measurement
# was taken against the expected duration and says nothing about
# this case, so a raised bar keeps the original 1.05.
_late_ratio = (
END_GATE_LATE_RATIO
if _bar_raised
else resolve_end_gate_late_ratio(self._config.device_type)
)
if not _blocked and _elapsed >= _late_ratio * _bar:
effective_off_delay = max(
self._config.off_delay,
min(self._config.min_off_gap, END_GATE_LATE_SECONDS),
)
# Energy gate always looks back off_delay seconds by default;
# overridden below for the dishwasher cap case so the window
# is consistent with the shortened effective_off_delay.
@@ -2491,6 +2902,46 @@ class CycleDetector:
current_duration = (timestamp - start).total_seconds()
if current_duration < self._expected_duration * STANDBY_BAND_MIN_RATIO:
return False
# #399 interaction, load-bearing since the gate above dropped from 2.0x to
# 1.0x expected (#445): a washer can sit quiet below anti_wrinkle_max_power
# for minutes BEFORE its final spin, and that quiet is a flat sub-10%-of-peak
# plateau like any other. Finalising there is exactly the failure #399 fixed
# - the spin then arrives and opens a second cycle record. Defer while the
# matched profile still owes this run its terminal high-power block. Shares
# the predicate with the anti-crease finalise so the two release together,
# and it fails open on every missing input (no profile block, non-terminal
# block, past the ANTI_CREASE_SPIN_WAIT_MAX_RATIO cap), so an appliance that
# never spins - the #445 Miele, which has no terminal block at all - is not
# delayed by it.
#
# **This used to be inert on the devices it exists for (register item
# 351).** `_anticrease_spin_pending` needs element 10, and the manager
# supplied it only when `anti_wrinkle_enabled` was true -
# `DEFAULT_ANTI_WRINKLE_ENABLED` is False, so on a washer the predicate
# returned False immediately and none of the above happened. Measured on
# the 273-cycle replay corpus: the band fired 14 times, 11 with the guard
# inert, and 6 of those 11 had a reading above `min_power` still ahead,
# i.e. would split. `terminal_high_for_guards` (module level in this file,
# shared by the manager's live match tuple and the Playground's sim tuple
# since round 31 - it used to be two hand-copies) now arms it for
# `STANDBY_BAND_FINALIZE_DEVICE_TYPES` against a share of the cycle's own
# peak - the same `STANDBY_BAND_MAX_FRACTION` used below, so the rule is
# "wait while the profile still owes a block above the plateau you are
# sitting on" and there is no new tunable. The bar travels WITH the block
# as element 4, because `_high_power_seconds_since` has to count live
# seconds above the same number.
#
# Measured end to end on `devtools/end_gate_eval.py`: washing-machine
# splits 4.49% -> 1.90%, median end lag 26.00 -> 24.74 min (it does not
# cost time - a cycle that used to split now finishes once), match rate
# 89.7% -> 92.4%, early ends unchanged at 0.00%, dishwashers identical.
# A ceiling of 0.15 caught the 6th split too but deferred 10 of the 11
# firings, and a deferral with no spin ahead waits out
# ANTI_CREASE_SPIN_WAIT_MAX_RATIO (1.25x expected, ~32 min on a 2:09
# wash), so it bought the last split for three long waits. 0.10 was the
# maintainer's call.
if self._anticrease_spin_pending(timestamp):
return False
peak = float(self._cycle_max_power)
if peak <= 0:
return False
@@ -2757,18 +3208,27 @@ class CycleDetector:
# exposure). The fallback keeps a pre-196 payload - an old state snapshot, the
# Playground, older callers - behaving exactly as before.
offset_s = float(block[2]) if len(block) >= 3 else start_frac * expected
seen = self._high_power_seconds_since(offset_s)
# Element 4, when the store sent one, is the watts the block was measured
# against. Count the live seconds above the SAME bar or the two halves
# describe different things (register item 351).
ceiling_w = float(block[3]) if len(block) >= 4 else None
seen = self._high_power_seconds_since(offset_s, ceiling_w=ceiling_w)
if seen >= needed:
return False
if not self._anticrease_spin_wait_logged:
self._anticrease_spin_wait_logged = True
self._logger.debug(
"Anti-crease finalize held: '%s' ends with a %.0fs block above %.0fW "
"at %.0f%% of its run (scanning from %.0fs); this cycle has %.0fs of "
"it so far (elapsed %.0fs of %.0fs expected).",
"Finalize held, terminal high-power block still owed: '%s' ends "
"with a %.0fs block above %.0fW at %.0f%% of its run (scanning "
"from %.0fs); this cycle has %.0fs of it so far (elapsed %.0fs of "
"%.0fs expected).",
self._matched_profile,
block_seconds,
float(self._config.anti_wrinkle_max_power),
(
float(self._config.anti_wrinkle_max_power)
if ceiling_w is None
else ceiling_w
),
start_frac * 100.0,
offset_s,
seen,
@@ -2777,9 +3237,16 @@ class CycleDetector:
)
return True
def _high_power_seconds_since(self, offset_s: float) -> float:
"""Seconds this cycle has spent above ``anti_wrinkle_max_power`` at or after
``offset_s`` from its start (#399).
def _high_power_seconds_since(
self, offset_s: float, ceiling_w: float | None = None
) -> float:
"""Seconds this cycle has spent above ``ceiling_w`` at or after ``offset_s``
from its start (#399); ``anti_wrinkle_max_power`` when None.
The caller supplies the ceiling so both halves of the comparison use one
bar: the profile's block was measured against it too. The standby-band
path passes a share of the cycle's own peak (register item 351), the
anti-crease path passes nothing and keeps the dryer's tumble level.
Walks the readings backwards and stops at the offset, so the scan is bounded
by the tail of the trace rather than its whole length. Each reading covers
@@ -2805,7 +3272,11 @@ class CycleDetector:
start = self._current_cycle_start
if start is None or not self._power_readings:
return 0.0
ceiling = float(self._config.anti_wrinkle_max_power)
ceiling = (
float(self._config.anti_wrinkle_max_power)
if ceiling_w is None
else float(ceiling_w)
)
max_gap = min(3600.0, max(60.0, 10.0 * self._prior_p95_dt))
total = 0.0
readings = self._power_readings
@@ -2864,12 +3335,18 @@ class CycleDetector:
# The tumble tail itself is never cut into, which is why this is safe
# here: an anti-crease baseline sits ABOVE stop_threshold
# (const.py:811-812, a ~2.5-3.2 W draw against a ~1.2 W threshold), so
# every one of those readings refreshes `_last_active_time` and the cap
# - max(expected_end, _last_active_time) - lands at the last tumble. A
# real tail therefore loses only the trailing quiet gap between its last
# reading and this finalize, which is time the appliance drew nothing.
# A tail of genuinely-0 W readings is clipped back to the matched
# profile's expected end. Shorten-only, never earlier than expected_end.
# every one of those readings refreshes `_last_active_time`, and for a
# non-dishwasher `_keep_tail_cap` returns exactly that - the last
# tumble. A real tail therefore loses only the trailing quiet gap
# between its last reading and this finalize, which is time the
# appliance drew nothing, and a tail of genuinely-0 W readings is
# dropped at `_last_active_time`.
#
# NB: the cap is no longer `max(expected_end, _last_active_time)` and
# so is NOT bounded below by the expected end - on a washer or dryer
# the stored end can now land earlier than the matched profile's
# expected duration. Shorten-only still holds; "never earlier than
# expected_end" no longer does, and this paragraph used to claim it.
tail_cap=self._keep_tail_cap(start_time),
)
return True
@@ -2921,14 +3398,42 @@ class CycleDetector:
# dishwasher cycle should never end before it has crossed the minimum
# reasonable programme duration. This prevents a dip during the fill or
# early wash phase from being read as the end of a complete cycle.
#
# A MATCHED profile overrides the blanket constant with its own learned
# length, because the constant is a stand-in for exactly the knowledge a
# match supplies - as this comment's own "even without a matched profile"
# says. Blanket, it is wrong for real hardware: the community catalogue
# carries a 6.0 min Smeg "Delay- prewash", which a 30 min floor defers by
# half an hour. The floor still applies unmatched, and a matched profile
# can only ever LOWER it (`min`), never license a longer deferral - the
# 39.4 min Electrolux "Rapido" already clears it and is unaffected.
#
# Gated on a TRUSTED match, not merely a present one. This floor is an
# anti-premature-end guard, so the risk is the opposite way round from
# the ENDING fallback gate (item 329, where a confidence check measured
# as pure cost): getting this wrong ends a dishwasher during its fill or
# early-wash dip and records the rest of the programme as a second
# cycle, which is the expensive failure. A low-confidence match to a
# short look-alike is exactly how that happens, so it does not get to
# lower the bar. Uses the WIDER `_match_prefix_ambiguous`, not the
# narrow full-shape flag Smart Termination takes: a false block here
# only keeps the 30 min floor, where for the anti-crease finalize it can
# re-hang the cycle (#296). No-op on the whole corpus either way - every
# corpus dishwasher profile is over 90 minutes.
_dw_floor = DISHWASHER_MIN_CYCLE_DURATION_S
if (
self._config.device_type == "dishwasher"
and duration < DISHWASHER_MIN_CYCLE_DURATION_S
self._matched_profile
and self._expected_duration > 0
and self._last_match_confidence >= self._config.match_confidence_threshold
and not self._match_ambiguous
and not self._match_prefix_ambiguous
):
_dw_floor = min(_dw_floor, float(self._expected_duration))
if self._config.device_type == "dishwasher" and duration < _dw_floor:
self._logger.debug(
"Deferring dishwasher cycle end: elapsed %.0fs < minimum %.0fs",
duration,
DISHWASHER_MIN_CYCLE_DURATION_S,
_dw_floor,
)
return True
@@ -3028,7 +3533,7 @@ class CycleDetector:
and self._expected_duration > 0
and not self._end_spike_seen
and duration
< (self._expected_duration + DISHWASHER_END_SPIKE_WAIT_SECONDS)
< (self._expected_duration + self._dishwasher_end_spike_wait_s())
and not quiet_released
):
# Report the gap-free tally: that is what `quiet_released` above reads,
@@ -3040,7 +3545,7 @@ class CycleDetector:
"%.0fs of %.0fs needed, profile: %s)",
duration,
self._expected_duration,
DISHWASHER_END_SPIKE_WAIT_SECONDS,
self._dishwasher_end_spike_wait_s(),
self._time_below_threshold_gapfree,
self._config.dishwasher_end_spike_quiet_release,
self._matched_profile,
@@ -3087,6 +3592,40 @@ class CycleDetector:
# Tertiary check: If duration exceeded max tolerance, allow finish (failsafe).
return False
def _dishwasher_end_spike_wait_s(self) -> float:
"""Grace past the expected end while waiting for the terminal pump-out.
Capped at the programme's OWN expected length - the same ``min()`` shape
register item 331 gave ``DISHWASHER_MIN_CYCLE_DURATION_S``, for the same
reason. A flat 1800 s is 20% of a 150 min ECO cycle but **five times** a
6 min Smeg "Delay- prewash", and the community catalogue carries exactly
that programme. Asymmetric: the cap can only ever SHORTEN the wait, never
extend it, so no cycle waits longer than it does today.
A no-op across the maintainer's corpus, where every dishwasher profile is
>90 min and the cap therefore never binds (register item 357). Unmatched
cycles keep the flat constant: with no expected duration there is nothing
to be proportional to.
"""
expected = float(self._expected_duration or 0.0)
if expected <= 0:
return DISHWASHER_END_SPIKE_WAIT_SECONDS
return min(DISHWASHER_END_SPIKE_WAIT_SECONDS, expected)
def _ending_hard_finalize_quiet_s(self) -> float:
"""Continuous sub-threshold span the ENDING backstop requires.
Same cap, same reason: 600 s of required quiet is a third of a 30 min
programme and longer than a 6 min one, which would disarm the backstop
entirely on a short programme - the opposite of what a safety net is for.
The ``off_delay`` / ``min_off_gap`` floor is applied by the caller and is
unaffected.
"""
expected = float(self._expected_duration or 0.0)
if expected <= 0:
return ENDING_HARD_FINALIZE_MIN_QUIET_S
return min(ENDING_HARD_FINALIZE_MIN_QUIET_S, expected)
def _keep_tail_cap(self, start_time: datetime) -> datetime | None:
"""Latest end time a *kept* tail may claim (#424).
@@ -3107,19 +3646,93 @@ class CycleDetector:
which delays the next Smart Termination further (the second reporter's
profile had already drifted 63 -> 70.5 min).
So cap the kept tail at whichever is later: the last above-threshold
reading, or the matched profile's expected end. Time after *both* is time
the appliance drew nothing AND that lies beyond the known length of the
programme it matched, so nothing real can live there. Asymmetric -
shorten-only, and never earlier than the expected end, so a genuine
passive drying phase still lands inside the stored cycle. Returns None
for an unmatched cycle, which has no expected end to anchor against and
is left exactly as before.
The cap used to be the matched profile's **expected end**. That is the
mean of these same stored durations, so it moved with the thing it was
bounding: a banked tail raised ``avg_duration``, the higher average
allowed a longer tail, and the reported end drifted later every run.
Measured over 375 cycles from 16 devices, smart-terminated cycles banked a
median **12.6 min** of post-appliance time (washing machines **22.7 min**,
p90 40.4 min) against ~0 min for every other termination path, and the
profiles carried a mean **+5.3%** duration inflation as a result - on the
#427 reporter's washer, +20.2 min on a 108 min programme, which is also
why their ETA read 131 min for a ~105 min wash.
So anchor on the last real activity instead. Where a passive phase can
legitimately follow it, allow only what this programme has been
**measured** to do (``profile_terminal_quiet_seconds``, element 11) - a
statistic taken from the traces, not from the stored durations, so it
cannot be inflated by the tail it bounds, and gated on having been seen
repeatedly rather than once.
**Only a dishwasher has a passive terminal phase.** Every other type ends
on activity - a washer's spin, a dryer's drum - which ``_last_active_time``
already marks, so there is nothing legitimate to bank after it. That is
not a new assumption: the fallback-timeout path beside this one has always
read ``keep_tail = device_type == "dishwasher"``. Smart Termination was
the one path that kept a tail for every type, which is exactly where the
22.7 min washing-machine median came from.
Within the dishwasher case, two sub-cases, and the difference matters:
* the run produced its terminal pump-out (``_end_spike_seen``).
``_last_active_time`` already sits on it, so that IS the end.
* it did not, so the programme ended in its passive drying phase. Allow up
to the profile's measured quiet span past the last activity.
Falls back to the old expected-end cap when that span has not been
measured, rather than truncating a drying phase on no evidence - the
measured corpus shows the pump-out missing in a substantial minority of
runs on some machines, and in those runs the drying IS the tail. Still
asymmetric and shorten-only; still None for an unmatched cycle.
"""
if self._expected_duration <= 0:
return None
expected_end = start_time + timedelta(seconds=self._expected_duration)
return max(expected_end, self._last_active_time or expected_end)
last_active = self._last_active_time
if last_active is None:
return expected_end
if self._config.device_type != DEVICE_TYPE_DISHWASHER:
return last_active
# Only a spike LATE enough to be the terminal pump-out licenses snapping
# the stored end back to the last activity. `_end_spike_seen` is set from
# DISHWASHER_END_SPIKE_MIN_PROGRESS (0.85), but this file does not treat
# every such spike as terminal: `_resolve_smart_ratio` relaxes its gate
# only at `>= expected * 0.90`, because below that the spike can be the
# pre-final-rinse drain with a passive Dry phase still to come. Capping
# at `last_active` for an 87% drain cuts that drying off, which lowers
# `avg_duration`, which makes the NEXT Smart Termination fire earlier -
# the error compounds in the direction that splits cycles. Same 0.90
# test here, so a pre-rinse drain falls through to the measured quiet
# span or the expected-end fallback below.
if getattr(self, "_end_spike_seen", False) and getattr(
self, "_end_spike_duration", 0.0
) >= self._expected_duration * 0.90:
return last_active
quiet = self._matched_terminal_quiet_s
if quiet is None:
return max(expected_end, last_active)
# ...and if the drying ALREADY happened, adding the allowance on top
# counts the same quiet twice. The 0.90 test above only catches a spike
# late enough to be unambiguously terminal; a pump-out at 85-90% of
# expected falls through it, and on a machine that dries BEFORE its final
# drain that banks a second drying period into the stored duration, which
# feeds `avg_duration` - the exact drift item 297 exists to remove.
# `ProfileStore.async_repair_banked_tails` has always asked the trace this
# question; this path did not, so the same cycle got one duration live and
# another when the repair re-judged it. Same helper, same 0.5 bar, so the
# two cannot drift again (register item 347).
if self._current_cycle_start is not None and self._power_readings:
_start = self._current_cycle_start
_pts = [
((ts - _start).total_seconds(), float(pw))
for ts, pw in self._power_readings
]
_last_off = (last_active - _start).total_seconds()
if quiet_run_before(
_pts, _last_off, self._config.stop_threshold_w
) >= 0.5 * float(quiet):
return last_active
return last_active + timedelta(seconds=min(quiet, TERMINAL_QUIET_CAP_S))
def _finish_cycle(
self,
@@ -3287,6 +3900,8 @@ class CycleDetector:
"match_prefix_ambiguous_full_shape": self._match_prefix_ambiguous_full_shape,
"matched_tail_power": self._matched_tail_power,
"matched_terminal_high": self._matched_terminal_high,
"matched_terminal_quiet_s": self._matched_terminal_quiet_s,
"longest_candidate_duration": self._longest_candidate_duration,
"ml_defer_start_duration": self._ml_defer_start_duration,
}
@@ -3360,6 +3975,17 @@ class CycleDetector:
self._matched_terminal_high = self._sanitize_terminal_high(
snapshot.get("matched_terminal_high")
)
self._matched_terminal_quiet_s = self._sanitize_terminal_quiet(
snapshot.get("matched_terminal_quiet_s")
)
# Unconditionally, because the hazard is the value already on the
# object, not the one in the snapshot: this restores the ambiguity
# flags the ENDING gate reads, so leaving the bound untouched pairs
# them with whatever a previous cycle left behind. A snapshot written
# before this key existed yields 0.0, i.e. the old refusal.
self._longest_candidate_duration = self._sanitize_longest_candidate(
snapshot.get("longest_candidate_duration")
)
self._ml_defer_start_duration = snapshot.get("ml_defer_start_duration")
# Restore state enter time and recompute time_in_state from it
@@ -52,6 +52,7 @@ _SENSITIVE_KEYS = {
"power_sensor",
"external_end_trigger",
"door_sensor_entity",
"unload_confirm_entity",
"switch_entity",
"energy_price_entity",
"energy_sensor",
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -21,5 +21,5 @@
"requirements": [
"numpy"
],
"version": "0.5.6"
}
"version": "0.5.7"
}
@@ -36,37 +36,60 @@ from typing import Any
import numpy as np
from .. import analysis
from ..const import (
DEFAULT_DTW_BANDWIDTH,
DEFAULT_DTW_MODE,
DEFAULT_PROFILE_MATCH_MAX_DURATION_RATIO,
DEFAULT_PROFILE_MATCH_MIN_DURATION_RATIO,
)
from ..signal_processing import resample_adaptive, resample_uniform
_RESAMPLE_L = 150
#: Matches the production matcher (``profile_store.async_match_profile``).
_MIN_DT = 5.0
_GAP_S = 21600.0
def _powers(cycle: dict[str, Any]) -> list[float]:
def _series(cycle: dict[str, Any]) -> tuple[np.ndarray, np.ndarray] | None:
"""``(offsets_s, watts)`` from a stored cycle, or None if unusable.
The offsets matter: ``analysis.find_best_alignment`` compares the two curves
**index by index** (its ``dt`` argument is explicitly unused), so both sides
must be on the same seconds-per-sample grid or the MAE compares different
moments of the cycle. Dropping the offsets - as this module did until register
item 303 - makes that impossible to honour.
"""
pd = cycle.get("power_data") or []
out: list[float] = []
ts: list[float] = []
pw: list[float] = []
for p in pd:
# Both values are converted BEFORE either is appended: a row whose offset
# parses and whose power does not would otherwise leave `ts` one element
# longer than `pw`, and `resample_uniform` raises inside `np.interp` on
# unequal arrays - a tuning run lost to one malformed sample.
# OverflowError too: `json` keeps an oversized integer literal as an
# unbounded int, and `float()` on one raises rather than returning inf.
try:
out.append(float(p[1]))
except (TypeError, ValueError, IndexError):
pass
return out
offset, watts = float(p[0]), float(p[1])
except (TypeError, ValueError, IndexError, OverflowError):
continue
ts.append(offset)
pw.append(watts)
if len(pw) < 4:
return None
return np.asarray(ts, dtype=float), np.asarray(pw, dtype=float)
def _resample(vals: list[float], n: int) -> np.ndarray:
a = np.asarray(vals, dtype=float)
if a.size == 0:
return np.zeros(n)
if a.size == n:
return a
return np.interp(np.linspace(0, 1, n), np.linspace(0, 1, a.size), a)
def _longest(segments: list[Any]) -> Any | None:
return max(segments, key=lambda s: len(s.power)) if segments else None
def _prep(cycles: list[dict[str, Any]]) -> dict[str, list[dict[str, Any]]]:
"""Group labelled cycles by profile, caching powers/duration/resampled curve."""
"""Group labelled cycles by profile, keeping the raw time series."""
by_profile: dict[str, list[dict[str, Any]]] = {}
for c in cycles:
name = c.get("profile_name")
pw = _powers(c)
if not name or len(pw) < 4:
series = _series(c)
if not name or series is None:
continue
try:
dur = float(c.get("duration"))
@@ -78,50 +101,216 @@ def _prep(cycles: list[dict[str, Any]]) -> dict[str, list[dict[str, Any]]]:
# sample every 30-60 s. Real cycles always carry a 'duration', so this only
# drops degenerate entries.
continue
ts, pw = series
review = c.get("ml_review")
golden = bool(review.get("golden")) if isinstance(review, dict) else False
by_profile.setdefault(name, []).append(
{"pw": pw, "dur": dur, "rs": _resample(pw, _RESAMPLE_L)}
{"ts": ts, "pw": pw, "dur": dur, "golden": golden}
)
return by_profile
def _snaps(by_profile: dict[str, list[dict]], exclude: tuple[str, int] | None) -> list[dict[str, Any]]:
def _regrid(item: dict[str, Any], dt: float, cache: dict) -> list[float] | None:
"""One cycle's curve on a ``dt``-second grid, cached per (cycle, dt)."""
key = (id(item), round(float(dt), 2))
if key in cache:
return cache[key]
seg = _longest(resample_uniform(item["ts"], item["pw"], dt_s=dt, gap_s=_GAP_S))
out = seg.power.tolist() if seg is not None and len(seg.power) >= 2 else None
cache[key] = out
return out
def _envelope_avg(
name: str, pool: list[dict[str, Any]], excluded: int | None, dt: float, cache: dict
) -> list[float] | None:
"""The pool's DTW-warped envelope average, re-gridded to the query's ``dt``.
This is what `ProfileStore.async_match_profile` scores against once a profile
has >= 2 confirmed cycles and no pinned golden cycle, i.e. the common case
(register item 347(e)).
Cheap enough to do honestly, which the deferral used to deny (item 354): an
envelope is built from traces, so it does not depend on the config being
tuned and survives the whole grid search, and leave-one-out changes only the
target's OWN profile - so the number of distinct envelopes is
``profiles + targets``, not ``folds x profiles``. Both caches below are keyed
accordingly.
"""
ekey = ("env", name, excluded)
built = cache.get(ekey)
if built is None:
raw = [
(it["ts"].tolist(), it["pw"].tolist(), float(it["dur"]))
for it in pool
]
try:
built = analysis.compute_envelope_worker(raw, _BASE_CFG["dtw_bandwidth"])
except Exception: # pylint: disable=broad-exception-caught
built = False # cache the failure; do not retry per target
cache[ekey] = built
if not built:
return None
grid, _lo, _hi, avg, _std, _target = built
if len(grid) < 2 or len(avg) != len(grid):
return None
gkey = ("envgrid", name, excluded, round(float(dt), 2))
if gkey in cache:
return cache[gkey]
seg = _longest(
resample_uniform(
np.asarray(grid, dtype=float), np.asarray(avg, dtype=float),
dt_s=dt, gap_s=_GAP_S,
)
)
out = seg.power.tolist() if seg is not None and len(seg.power) >= 2 else None
cache[gkey] = out
return out
def _snaps(
by_profile: dict[str, list[dict]],
exclude: tuple[str, int] | None,
dt: float,
cache: dict,
) -> list[dict[str, Any]]:
"""One snapshot per profile, re-gridded to the QUERY's ``dt``.
Mirrors production: ``profile_store`` resamples the current cycle with
``resample_adaptive`` and then re-grids every candidate to that same
``used_dt`` via ``_get_cached_sample_segment``, so index *i* is the same
elapsed time on both sides.
**The template follows production's three-way rule, in production's order**
(`ProfileStore.async_match_profile`), which is register item 356 closing item
347(e): a pinned golden cycle's own sharp trace, else the DTW-warped ENVELOPE
AVERAGE once the pool holds >= 2 cycles, else the single representative
sample - which is also the safety net when an envelope will not build. The
middle branch is the common case, and it is the one that used to be missing:
scoring one representative cycle where live matching scores the envelope
average can favour weights that win on a trace nothing is matched against.
**Cost, and the trap inside it.** An envelope is built from traces, so it does
not depend on the weights being tuned and survives the whole grid search plus
every holdout call; and leave-one-out excludes exactly ONE cycle, so only the
target's own profile gets a different template while every other profile
keeps the full-pool one, shared by every target. Distinct templates are
therefore `profiles + targets`, not `folds x profiles` - measured on the worst
real export in `cycle_data/` (12 profiles with >= 2 cycles, 63 targets), 75
templates at 79 ms a DTW warp, ~5.9 s one-off. **That figure only holds if the
cache outlives one `_top1` call.** It did not at first: `_top1` built a fresh
cache and is called ~59 times (1 base + 48 grid + 10 holdout), which measured
16.1 s -> 360.6 s on a real export. Hence the `cache` argument threaded
through the whole run - nothing in it depends on `cfg`. Do not re-scope it.
The cheap approximation (averaging the pool's regridded curves without the
DTW warp) is measured WORSE and is not what `_envelope_avg` does.
The exposure is bounded regardless: `tune_matching_config` can
only move the bounded scoring weights, never structural matching behaviour,
`revert_matching_config` undoes it, and promotion is gated on **held-out
top-1 accuracy**: `tune_matching_config` promotes only when the tuned config
beats the baseline by `margin` on at least `min_wins` of `n_splits` held-out
subsamples (4 of 5) AND the mean held-out top-1 gain is itself >= `margin`.
Not AUC - no AUC is computed anywhere in this module. The AUC gate in
CLAUDE.md is `ML_TRAINING_AUC_MARGIN`, which governs the ml/ CLASSIFIERS in
`training_task.py` and has nothing to do with the matcher's scoring weights.
"""
snaps = []
for name, items in by_profile.items():
curves, durs = [], []
for idx, it in enumerate(items):
if exclude is not None and (name, idx) == exclude:
continue
curves.append(it["rs"])
durs.append(it["dur"])
if curves:
snaps.append({
"name": name,
"avg_duration": float(np.mean(durs)),
"sample_power": np.mean(np.array(curves), axis=0).tolist(),
})
excluded = exclude[1] if (exclude is not None and exclude[0] == name) else None
pool = [
it for idx, it in enumerate(items)
if exclude is None or (name, idx) != exclude
]
if not pool:
continue
durs = [it["dur"] for it in pool]
avg = float(np.mean(durs))
# Production's three-way template rule, in production's order
# (`ProfileStore.async_match_profile`). Getting this wrong is register
# item 347(e): scoring one representative cycle where live matching
# scores the envelope average can favour weights that lose on the curve
# that actually does the matching.
curve = None
golden = [it for it in pool if it.get("golden")]
if golden:
# 1. a pinned golden cycle keeps its own sharp trace; the average
# smears the wash-phase peaks, which is why production prefers it.
curve = _regrid(golden[0], dt, cache)
elif len(pool) >= 2:
# 2. the common case: the DTW-warped envelope average.
curve = _envelope_avg(name, pool, excluded, dt, cache)
if not curve:
# 3. the single-sample fallback, and the safety net for a profile
# whose envelope will not build (too few usable points, a trace
# that resamples to nothing). Representative rather than
# arbitrary: closest to the pool's mean duration.
rep = min(pool, key=lambda it: abs(it["dur"] - avg))
curve = _regrid(rep, dt, cache)
if not curve:
continue
snaps.append({
"name": name,
"avg_duration": avg,
"sample_power": curve,
})
return snaps
def _top1(by_profile: dict[str, list[dict]], targets: list[tuple[str, int]], cfg: dict[str, Any]) -> float:
def _top1(
by_profile: dict[str, list[dict]],
targets: list[tuple[str, int]],
cfg: dict[str, Any],
cache: dict | None = None,
) -> float:
"""Fraction of the given (profile, idx) targets whose true profile ranks #1
under leave-one-out matching with the given config."""
under leave-one-out matching with the given config.
``cache`` is shared ACROSS calls on purpose. Nothing in it depends on
``cfg``: the re-gridded curves are keyed by (cycle, dt) and the envelopes by
(profile, excluded index), while ``cfg`` only changes the scoring weights.
Left per-call - as it was - the grid search rebuilds every envelope for each
of its ~59 configs, which measured 16 s -> 361 s on one real export. This is
what makes the `profiles + targets` cost in register item 354 real rather
than theoretical."""
if not targets:
return 0.0
correct = 0
total = 0
if cache is None:
cache = {}
for name, idx in targets:
it = by_profile[name][idx]
snaps = _snaps(by_profile, exclude=(name, idx))
# The query defines the grid, exactly as in production.
segments, used_dt = resample_adaptive(
it["ts"], it["pw"], min_dt=_MIN_DT, gap_s=_GAP_S
)
seg = _longest(segments)
if seg is None or len(seg.power) < 4:
continue
snaps = _snaps(by_profile, (name, idx), used_dt, cache)
if len(snaps) < 2:
continue
cands = analysis.compute_matches_worker(it["pw"], it["dur"], snaps, cfg)
cands = analysis.compute_matches_worker(
seg.power.tolist(), it["dur"], snaps, cfg
)
total += 1
if cands and cands[0]["name"] == name:
correct += 1
return correct / total if total else 0.0
_BASE_CFG = {"min_duration_ratio": 0.10, "max_duration_ratio": 1.5}
# The full production matcher config. A PARTIAL config does not inherit the
# production defaults - `compute_matches_worker` has its own fallbacks - so
# omitting a key here would tune the weights against a pipeline that never
# ships. `energy_mode` is added per device type at the call site.
_BASE_CFG = {
"min_duration_ratio": DEFAULT_PROFILE_MATCH_MIN_DURATION_RATIO,
"max_duration_ratio": DEFAULT_PROFILE_MATCH_MAX_DURATION_RATIO,
"dtw_bandwidth": DEFAULT_DTW_BANDWIDTH,
"dtw_mode": DEFAULT_DTW_MODE,
}
#: Bounded scoring weights the tuner may promote. All live in [0, 1], so a tuned
#: config can only shift emphasis (shape vs level vs energy, and how much the DTW
@@ -211,10 +400,13 @@ def tune_matching_config(
# so promoted weights are consistent with the live matcher.
base = {**_BASE_CFG, "energy_mode": analysis.stage4_energy_mode(device_type)}
# Candidate: the grid config with the best top-1 on the SEARCH pool only.
best_search = _top1(by_profile, search_pool, base)
# One cache for the entire run: the grid search and both holdout arms all
# reuse the same envelopes and re-gridded curves (see `_top1`).
shared: dict = {}
best_search = _top1(by_profile, search_pool, base, shared)
best_cfg = base
for extra in _grid():
acc = _top1(by_profile, search_pool, {**base, **extra})
acc = _top1(by_profile, search_pool, {**base, **extra}, shared)
if acc > best_search:
best_search, best_cfg = acc, {**base, **extra}
override = {k: best_cfg[k] for k in OVERRIDE_KEYS if k in best_cfg}
@@ -231,8 +423,8 @@ def tune_matching_config(
pool = list(holdout_pool)
r.shuffle(pool)
held = pool[: max(1, len(pool) // 2)]
bt = _top1(by_profile, held, base)
tt = _top1(by_profile, held, best_cfg)
bt = _top1(by_profile, held, base, shared)
tt = _top1(by_profile, held, best_cfg, shared)
base_tests.append(bt)
tuned_tests.append(tt)
if tt - bt >= margin:
@@ -246,6 +438,21 @@ def tune_matching_config(
if promoted:
reason = f"beat baseline on {wins}/{n_splits} held-out subsamples"
elif not has_override:
# AUDITED and correct (register item 356), because it fires on every real
# export in `cycle_data/` and that looks like a stuck mechanism. It is
# not. `_BASE_CFG` deliberately omits the four OVERRIDE_KEYS, so
# `compute_matches_worker` falls back to `MATCH_CORR_WEIGHT` /
# `MATCH_DURATION_WEIGHT` / `MATCH_ENERGY_WEIGHT` /
# `MATCH_DTW_ENSEMBLE_W` - the tuner's baseline IS production's default,
# not a partial config with different fallbacks. `override` is therefore
# empty exactly when no grid entry beat that baseline, which is what this
# string says. The weights do reach the scorer: on the least saturated
# real device (12 profiles, 63 targets, base top-1 0.635) the grid
# produces three distinct scores, all <= base. The grid also contains the
# exact default combination (0.45 / 0.22 / 0.22 / 0.70), so the defaults
# are evaluated on equal terms, and `acc > best_search` is strict so a tie
# keeps them. Consistent with the corpus result that matcher top-1 is
# near-saturated: there is nothing here for per-device tuning to win.
reason = "defaults already optimal (no override)"
elif not enough_wins:
reason = f"only {wins}/{n_splits} held-out subsamples beat baseline by margin"
+48 -9
View File
@@ -130,11 +130,13 @@ from .cycle_detector import (
CycleDetectorConfig,
effective_anticrease_finalize_ratio,
effective_curve_preroll_seconds,
terminal_high_for_guards,
)
from .profile_store import (
_ambiguity_from_candidates,
_match_prefix_ambiguity,
collapse_group_candidates,
longest_candidate_duration,
decompress_power_data,
)
@@ -974,6 +976,10 @@ class _DetailSim:
# Mirror of async_match_profile: members are scored individually, then each
# cohesive family is collapsed to its best member before anything reads the
# ranking (#400).
# Captured before the collapse for element 12, exactly as
# async_match_profile does: the collapse drops every sibling but the
# best, and a longer one leaving the list lowers the ENDING gate's bar.
pre_collapse_candidates = list(candidates)
candidates = collapse_group_candidates(candidates, self.group_members or {})
# Stage-5 safeguards #2 and #3, captured here and applied after the
# top-level ambiguity call below so the ORDER matches async_match_profile.
@@ -1068,16 +1074,34 @@ class _DetailSim:
tail_power = self.store.profile_tail_power(raw_name)
except Exception: # pylint: disable=broad-exception-caught
tail_power = None
# Element 10 (#399), guarded the same way: the anti-crease spin guard is
# only meaningful for a device that runs anti-wrinkle at all.
# Element 10 (#399) and the standby-band arm on top of it (register
# item 351). This used to be a hand-copy of the manager's version, and
# the two had already drifted in their error handling; it is now the
# SAME function, which is the only way this replay can be guaranteed
# byte-identical to live on element 10. `end_gate_eval.py` drives the
# detector through here, so an arm present in only one of them is
# invisible to every measurement made with that harness - exactly how
# item 352 first measured as a no-op.
det = getattr(self, "detector", None)
if det is not None and det.config.anti_wrinkle_enabled:
try:
terminal_high = self.store.profile_terminal_high_block(
raw_name, det.config.anti_wrinkle_max_power
)
except Exception: # pylint: disable=broad-exception-caught
terminal_high = None
if det is not None:
terminal_high = terminal_high_for_guards(
self.store,
det.config,
getattr(det, "_cycle_max_power", 0.0),
raw_name,
)
# Element 11 (item 297) and element 12 (item 330). Both were missing, so
# the sim's detector ran without the tail bound Smart Termination uses and
# without the longest-candidate bar the ENDING gate uses - i.e. the replay
# was NOT byte-identical to live on either, which is the sim's whole
# contract. `end_gate_eval.py` drives the detector through here, so a
# missing element silently measures the wrong gate.
terminal_quiet = None
if self.store is not None and raw_name:
try:
terminal_quiet = self.store.profile_terminal_quiet_seconds(raw_name)
except Exception: # pylint: disable=broad-exception-caught
terminal_quiet = None
return (
raw_name,
raw_conf,
@@ -1089,6 +1113,14 @@ class _DetailSim:
bool(full_shape_hit),
tail_power,
terminal_high,
terminal_quiet,
# The FULL population, matching live: `async_match_profile` computes
# `MatchResult.longest_candidate_duration_s` from the candidates as
# they stood BEFORE the group collapse and before the [:5]
# truncation. An earlier version of this line used `candidates[:5]`
# to match what live *then* did - and live was the thing that was
# wrong, twice.
longest_candidate_duration(pre_collapse_candidates),
)
def _price_at(self, offset_s: float) -> float | None:
@@ -1137,6 +1169,7 @@ class _DetailSim:
offset = (ts - self.base).total_seconds()
if offset - self.last_sample_t < _SIM_SERIES_THROTTLE_S:
return
prev_sample_t = self.last_sample_t
self.last_sample_t = offset
state = self.detector.state
power = 0.0
@@ -1184,6 +1217,12 @@ class _DetailSim:
result = progress_mod.compute_progress(
self.device_type, matched_dur, offset, self.smoothed["v"], phase_result, ml_pct,
phase_remaining_s=phase_remaining_s,
# Same time-scaled smoothing as live: the sim steps the estimator
# at its own throttle, so without this the replay would smooth
# over 30 s steps as if they were the manager's 5 s ones.
dt_seconds=(
offset - prev_sample_t if prev_sample_t >= 0.0 else None
),
)
if result is not None:
self.smoothed["v"] = result.smoothed
File diff suppressed because it is too large Load Diff
+42 -2
View File
@@ -55,6 +55,10 @@ _LOGGER = logging.getLogger(__name__)
# class constant of the same purpose).
PROJECTION_MIN_PROGRESS = 3.0
# The progress EMA weights below are per *estimate*, and were chosen against the
# manager's 5 s estimate throttle. See :func:`_dt_scaled_alpha`.
SMOOTHING_NOMINAL_DT_S = 5.0
# Cache type for profile_end_expectation: (profile_name, base_expectation_dict).
EndExpCache = tuple[str, dict[str, float]] | None
@@ -581,6 +585,36 @@ def estimate_phase_progress(
return (best_progress, best_variance)
def _dt_scaled_alpha(alpha: float, dt_s: float | None) -> float:
"""Rescale a per-estimate EMA weight to the real interval between estimates.
A first-order filter trails a ramp by ``slope * (1 - a) / a`` per step, and
progress IS a ramp, so the steady-state lag is set by how much progress the
cycle makes between two estimates. Estimates are driven by power-sensor
events, not by a clock: a plug reporting every 30 s advances 6x more per step
than the 5 s throttle these weights were picked for, so the lag grows with it.
Measured on a 149 min dishwasher whose estimates landed ~3 min apart, the
linear branch sat ~14pp behind - back-calculated as ~20 min of remaining time
that never ran out, so the countdown stalled at "20 minutes left" through the
whole tail and the overrun handover (which waits for remaining to reach 0)
never fired. Replaying that cadence: 83.7% / 23.9 min left at the moment the
cycle ended, against 100% / 0 with the weight rescaled.
Rescaling holds the *time* constant instead of the step count::
alpha_dt = 1 - (1 - alpha) ** (dt / SMOOTHING_NOMINAL_DT_S)
``dt_s`` of ``None`` (or <= 0) keeps the nominal weight, so every caller that
does not track its own cadence - and the golden snapshot - is unchanged.
"""
if dt_s is None or not math.isfinite(dt_s) or dt_s <= 0.0:
return alpha
if alpha <= 0.0 or alpha >= 1.0:
return alpha
steps = float(dt_s) / SMOOTHING_NOMINAL_DT_S
return 1.0 - (1.0 - alpha) ** steps
def _compute_progress_base(
device_type: str,
matched_duration: float,
@@ -589,6 +623,7 @@ def _compute_progress_base(
phase_result: tuple[float, float] | None,
ml_pct: float | None,
logger: logging.Logger | None = None,
dt_seconds: float | None = None,
) -> ProgressResult | None:
"""The blend + EMA + monotonicity + back-calculation body of the estimate loop.
@@ -629,6 +664,8 @@ def _compute_progress_base(
smoothing_threshold = DEVICE_SMOOTHING_THRESHOLDS.get(device_type, 5.0)
if phase_progress < current_smoothed - smoothing_threshold:
# Backward step: damping here exists to resist regression, not to
# track, so it stays per-estimate (unscaled) on purpose.
smoothed = (current_smoothed * 0.95) + (phase_progress * 0.05)
logger.debug(
"Progress drop detected (%.1f%% < %.1f%% - %.1f%%), "
@@ -639,6 +676,7 @@ def _compute_progress_base(
device_type,
)
else:
alpha = _dt_scaled_alpha(alpha, dt_seconds)
smoothed = (prev_smoothed * (1.0 - alpha)) + (phase_progress * alpha)
smoothed = min(99.0, smoothed)
@@ -667,7 +705,8 @@ def _compute_progress_base(
remaining = max(matched_dur * (1.0 - progress / 100.0), 0.0)
if prev_smoothed > 0:
smoothed = (prev_smoothed * 0.9) + (progress * 0.1)
lin_alpha = _dt_scaled_alpha(0.1, dt_seconds)
smoothed = (prev_smoothed * (1.0 - lin_alpha)) + (progress * lin_alpha)
else:
smoothed = progress
@@ -691,6 +730,7 @@ def compute_progress(
ml_pct: float | None,
logger: logging.Logger | None = None,
phase_remaining_s: float | None = None,
dt_seconds: float | None = None,
) -> ProgressResult | None:
"""Progress/remaining estimate, optionally blended with a phase-resolved ETA.
@@ -741,7 +781,7 @@ def compute_progress(
base = _compute_progress_base(
device_type, matched_duration, duration_so_far, prev_smoothed,
phase_result, ml_pct, logger,
phase_result, ml_pct, logger, dt_seconds,
)
if base is None or not blended:
return base
@@ -272,6 +272,18 @@ resume_cycle:
device:
integration: ha_washdata
mark_unloaded:
name: Mark Unloaded
description: Confirm the finished load has been taken out, clearing the Clean state and any unload reminder. Does nothing when no load is waiting.
fields:
device_id:
name: Device
description: The WashData device whose finished load has been taken out.
required: true
selector:
device:
integration: ha_washdata
trigger_ml_training:
name: Trigger ML Training
description: Manually retrain the on-device ML models from this device's own labelled cycles (experimental; requires ML training enabled).
@@ -441,3 +441,37 @@ def resample_adaptive(
return segments, target_dt
def quiet_run_before(
points: list[tuple[float, float]], last_active: float, stop_threshold_w: float
) -> float:
"""Length of the sub-threshold run immediately preceding ``last_active``.
Recognises a terminal pump-out from the trace alone: if the final
above-threshold sample is preceded by a long quiet stretch, that sample is
the event the drying led up to, not the last moment of washing. Returns 0.0
when the sample before it is still active.
Shared on purpose. The banked-tail repair and ``CycleDetector._keep_tail_cap``
have to answer this question the same way, or a cycle gets one allowance live
and a different one when the repair re-judges it (register item 347).
"""
# The terminal event is a RUN of above-threshold samples, not one sample, so
# walk back to where that run starts before measuring the quiet in front of
# it. Measuring from `last_active` itself finds only the run's own width.
idx = [i for i, (o, _p) in enumerate(points) if o <= last_active]
if not idx:
return 0.0
i = idx[-1]
while i > 0 and points[i - 1][1] > stop_threshold_w:
i -= 1
run_start = points[i][0]
# Now the last above-threshold sample before that run, if any.
prev_active: float | None = None
for offset, power in points:
if offset >= run_start:
break
if power > stop_threshold_w:
prev_active = offset
if prev_active is None:
return 0.0
return max(0.0, run_start - prev_active)
@@ -280,6 +280,16 @@
"description": "The WashData device to retrain models for."
}
}
},
"mark_unloaded": {
"name": "Mark Unloaded",
"description": "Confirm the finished load has been taken out of a WashData device, clearing the Clean state and any unload reminder.",
"fields": {
"device_id": {
"name": "Device",
"description": "The WashData device whose finished load has been taken out."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Record Cycle Stop"
},
"mark_unloaded": {
"name": "Mark Unloaded"
}
}
},
@@ -29,6 +29,7 @@ import numpy as np
from homeassistant.core import HomeAssistant, callback
from .const import (
TerminationReason,
CONF_WATCHDOG_INTERVAL,
CONF_NO_UPDATE_ACTIVE_TIMEOUT,
CONF_OFF_DELAY,
@@ -221,6 +222,98 @@ def _resumed_low_runs(
return out
def _measured_quiet_span_s(
points: list[tuple[float, float]],
low_start_s: float,
resume_idx: int,
quiet_thr: float,
) -> float:
"""Longest quiet span inside a resumed low run, as the DETECTOR would time it.
``_resumed_low_runs`` locates a pause by asking when the appliance went below
``active_thr`` and when it came back - the right question for *whether* this
was a pause. It is the wrong measurement for ``off_delay``, because the run
is closed only by a *sustained* resume (``_MIN_RESUME_ACTIVE_S``, 120 s) and
any dip re-absorbs the blip. An appliance that works in bursts shorter than
that never closes a run: measured on the #445 reporter's Miele (10 s
sampling, ~110 W tumble for 20-60 s alternating with 3.4 W dips), the *first*
dip opened a run that stayed open for 2070 s until a long heating block
finally arrived - and that "pause" contains a 497.9 W peak. The p95 came out
at 1973 s, so the suggestion asked for ``off_delay`` 2033 s on a machine
whose real pauses are one sample long. Retuning ``active_thr`` does not help:
swept over ``0.02*peak`` / ``stop_threshold`` / ``start_threshold`` /
``0.25*median_active`` / ``0.5*median_active``, that p95 stays 1944-1976 s.
So measure what ``CycleDetector._time_below_threshold`` would actually have
accumulated: it resets on *any* reading at or above ``stop_threshold_w``, so
the statistic is the longest run of consecutive below-threshold samples. A
run whose samples never go below it is not a pause the end gates could ever
have seen, and returns 0.0 for the caller to drop.
Timed like the accumulator too: the interval *preceding* the first
below-threshold sample is credited (the detector adds ``dt`` on the reading
that takes it below), so a span is ``t[last_below] - t[first_below - 1]``.
"""
return _measured_quiet_span(points, low_start_s, resume_idx, quiet_thr)[0]
def _measured_quiet_span(
points: list[tuple[float, float]],
low_start_s: float,
resume_idx: int,
quiet_thr: float,
) -> tuple[float, int | None]:
""":func:`_measured_quiet_span_s`, plus the index the winning span ends on.
The caller needs both, and they can disagree. The low run is bounded by
``active_thr = max(stop_threshold_w, 0.05 * peak)``, so on any normal
appliance (peak 2 kW, stop threshold ~2.5 W) a reading anywhere between
those two levels splits the run into several below-threshold spans. This
function returns the LONGEST of them, while ``points[:resume_idx]`` ends on
the LAST one - so scoring that prefix described a different span from the
duration reported beside it, and ``_ml_off_delay``'s ``score < 0.4`` filter
admitted or rejected the wrong durations.
"""
if resume_idx <= 0 or resume_idx > len(points):
return 0.0, None
best = 0.0
best_end: int | None = None
first_below: int | None = None
last_below: int | None = None
def _close(f: int, last: int) -> None:
nonlocal best, best_end
anchor_t = points[max(0, f - 1)][0]
# `_resumed_low_runs` drops a low run that straddles a gap bigger than
# `_MAX_PAUSE_GAP_H` and opens a new one at the first reading after it.
# If that reading is already below the threshold, anchoring at `f - 1`
# reaches back across the outage and folds the whole hole into the span
# - which then flows into the p95 that sizes `off_delay` in both
# `_suggest_off_delay_from_pauses` and `_ml_off_delay`, where three
# pauses are enough for one to dominate. That is precisely the inflation
# the `max_gap_s` guard exists to prevent, arriving by the back door.
if points[f][0] - anchor_t > _MAX_PAUSE_GAP_H * 3600:
anchor_t = points[f][0]
span = points[last][0] - anchor_t
if span > best:
best, best_end = span, last
for i in range(resume_idx):
t, power = points[i]
if t < low_start_s:
continue
if power < quiet_thr:
if first_below is None:
first_below = i
last_below = i
elif first_below is not None and last_below is not None:
_close(first_below, last_below)
first_below = last_below = None
if first_below is not None and last_below is not None:
_close(first_below, last_below)
return max(0.0, best), best_end
def _cycle_readings(cycle: dict[str, Any]) -> list[tuple[float, float]]:
"""Normalise a cycle's power_data to [(offset_s, watts), ...]; [] on failure."""
raw = cycle.get("power_data")
@@ -347,6 +440,18 @@ def select_clean_cycles(
if status == "force_stopped":
_bump("force_stopped")
continue
# A cycle the user cut short with "Force cycle end" is stored
# status="completed", termination_reason="user" (CycleDetector.user_stop),
# so the force_stopped check above never sees it (#445). Its tail is
# whatever the appliance happened to be doing when the button was pressed
# - usually minutes of standby the user got tired of waiting through - so
# every statistic derived from it (sampling cadence, lowest active power,
# clean-cycle duration, intra-cycle pauses) describes the user's patience,
# not the appliance. Excluded under its own code so the UI can say which
# of the two it was.
if c.get("termination_reason") == TerminationReason.USER:
_bump("user_stopped")
continue
if status == "interrupted" or state == "interrupted":
_bump("interrupted")
continue
@@ -389,6 +494,159 @@ def select_clean_cycles(
return clean, excluded
# A final reading only counts as standby when it is at most this fraction of the
# cycle's own peak. Sized to keep every real case in the corpus (the #445
# reporter's 3.2 W idle against a ~2 kW peak is 0.16%) while rejecting a cycle
# stopped by hand while the appliance was still working.
_STANDBY_PEAK_FRACTION = 0.10
# ...and the observed levels must agree with each other, measured as the median
# absolute deviation over the median. Outlier-ROBUST on purpose: min/max spread
# was tried and rejected because #445's own Miele has one 7.5 W reading among
# [4.1, 3.4, 7.5, 3.2, 3.4] and that single sample put it at 1.26, rejecting the
# canonical true positive. On MAD the same device scores 0.059 while the corpus
# washing machine that also passes the all-above test - [8.4, 7.7, 27.0, 8.8,
# 55.0, 26.8, 6.0, 38.9], where the last stored sample is just wherever the drum
# stopped - scores 0.548. 0.25 sits with ~4x margin on both sides.
_STANDBY_MAX_MAD = 0.25
def detect_standby_above_stop(
cycles: list[dict[str, Any]],
stop_threshold_w: float,
*,
recent: int = 8,
min_hits: int = 2,
) -> dict[str, Any] | None:
"""Does this appliance idle ABOVE its stop threshold? (#445 cause 1)
``CycleDetector`` only counts a cycle as ending once power stays BELOW
``stop_threshold_w``. An appliance whose standby draw sits above it can
therefore never end a cycle on its own, however long the off delay: the #445
reporter's Miele idles at 3.2-3.5 W against a 2.56 W threshold, and they
force-stopped four cycles before reporting it.
The evidence is in the stored cycles. A cycle that ended by timeout snaps back
to the last reading above the threshold, and one the user force-stopped keeps
its tail - so in both cases the LAST stored sample is the level the appliance
was actually sitting at when the cycle closed. If that is repeatedly above
``stop_threshold_w``, the threshold is below the appliance's standby draw.
**Two requirements, and both are load-bearing** (PR #448 round 7). The first
draft asked only that ``min_hits`` of the recent cycles ended above the
threshold, and a peak-relative sanity check was added on top. Measured across
the whole corpus that still fired on **4 of 6 real devices** with plainly
wrong numbers - it told a dishwasher whose last stored samples are
``[0, 0, 0, 0, 0, 0, 62, 23]`` that it "idles at 42.5 W". The reason is
structural: every non-dishwasher end path TRIMS the trailing sub-threshold
samples, so the last stored sample is by construction the last sample *above*
the threshold - the moment the appliance was last working, not the level it
settled at. On a machine that really reaches 0 W that number is meaningless.
So:
1. **Every** recent cycle must end above the threshold, not merely
``min_hits`` of them. One cycle that reached 0 W proves the appliance
*can* go below, which is the whole question being asked.
2. The level must be **consistent**, as median-absolute-deviation over the
median, within ``_STANDBY_MAX_MAD``. A real standby draw is a level;
"wherever the drum happened to be" ranges 6 W to 55 W across eight cycles,
which is what the corpus's washing machines actually look like.
**Validated against the reporters' own exports, not just the corpus.** Of
nine real devices only one passes both tests: #445's Miele, every cycle
ending 3.2-7.5 W against a 2.56 W threshold (MAD 0.059). The corpus washing
machine that also never reaches 0 W is rejected on consistency (MAD 0.548).
**#427's AEG and #424's Beko are deliberately NOT reported**, though an
earlier version of this docstring cited them as validation at 4/8 each. Their
stored cycles end ``[0.0, 1.9, 0.1, 0.9, 0.8, 0.0, 0.0, 0.7]`` and
``[1.2, 1.3, 1.3, 0.0, 0.3, 0.4, 0.2, 1.3]`` - both reach 0 W regularly, so
neither idles above its threshold. Their late finishes had a different cause
(the keepalive cadence bug, fixed separately), and counting them here was
reading a coincidence as a diagnosis.
User-stopped cycles are still kept: on an appliance with this fault they are
often the ONLY way a cycle ever closes, and the #445 reporter force-stopped
four.
Returns None when there is no such pattern, else a summary carrying the
observed idle level so the UI can name a number rather than a symptom. Pure
statistics, never raises: this is read on the device-list path.
"""
try:
if stop_threshold_w <= 0:
return None
finals: list[float] = []
for cycle in list(cycles)[-recent:]:
if not isinstance(cycle, dict):
continue
raw = cycle.get("power_data")
if not isinstance(raw, list) or not raw:
continue
last = raw[-1]
if not isinstance(last, (list, tuple)) or len(last) < 2:
continue
try:
final_w = float(last[1])
except (TypeError, ValueError, OverflowError):
continue
# The stored signature already carries this cycle's peak, and it is
# what the rest of the integration means by one (Stage 1 rejection
# reads the same field), so scanning every sample again is pure
# waste. It is not free waste: this runs on the event loop inside the
# synchronous `ws_get_devices` callback, which the panel polls every
# 20 s per device, and measured over the worst real export in
# `cycle_data/` (11454 samples across the last 8 cycles) the scan
# costs 1.73 ms a call. Not a hazard at that cadence, which is why
# there is no cache here: keying one on cycle count, last id and
# threshold buys a millisecond and adds an invalidation path that can
# serve a stale advisory. Falling back to the scan keeps a cycle
# whose signature is missing or unusable working exactly as before.
peak = 0.0
_sig = cycle.get("signature")
_sig_peak = _sig.get("max_power") if isinstance(_sig, dict) else None
try:
_sig_peak = float(_sig_peak) if _sig_peak is not None else None
except (TypeError, ValueError, OverflowError):
_sig_peak = None
if _sig_peak is not None and math.isfinite(_sig_peak) and _sig_peak >= 0:
peak = _sig_peak
else:
for pt in raw:
if isinstance(pt, (list, tuple)) and len(pt) >= 2:
try:
peak = max(peak, float(pt[1]))
except (TypeError, ValueError, OverflowError):
continue
# Idle-like or nothing: a cycle stopped by hand mid-wash ends at
# working power and says nothing about the standby draw.
if peak > 0 and final_w > peak * _STANDBY_PEAK_FRACTION:
continue
finals.append(final_w)
if len(finals) < min_hits:
return None
above = [f for f in finals if f > stop_threshold_w]
# Requirement 1: EVERY recent cycle ended above the threshold. One that
# reached it proves the appliance can, so it does not idle above it.
if len(above) != len(finals) or len(above) < min_hits:
return None
median = float(np.median(above))
if median <= 0:
return None
# Requirement 2: the levels agree with each other. See _STANDBY_MAX_MAD.
mad = float(np.median([abs(f - median) for f in above]))
if mad / median > _STANDBY_MAX_MAD:
return None
return {
"cycles_above": len(above),
"cycles_checked": len(finals),
"idle_w": round(median, 2),
"stop_threshold_w": round(float(stop_threshold_w), 2),
}
except Exception: # noqa: BLE001 - a statistic must never break the device list
return None
def _format_exclusions(excluded: dict[str, int]) -> str:
"""English exclusion note for the suggestion ``reason`` fallback string.
@@ -1358,7 +1616,16 @@ class SuggestionEngine:
# same sustained-resume + outage-gap gate used by the off_delay
# heuristics (_suggest_off_delay_from_pauses / _scored_pauses).
for low_start_s, resume_idx in _resumed_low_runs(readings, active_thr, max_gap_s):
if readings[resume_idx][0] - low_start_s >= 60.0:
# Same measurement correction as the off_delay heuristic (#445):
# a "false end" is quiet time the END GATES would have banked, so
# it is timed against stop_threshold_w, not against the
# 2%-of-peak activity floor that only decides whether this was a
# pause at all. Without it, an appliance that works in bursts
# scores a false end in every cycle and end_repeat_count is
# driven up on evidence the detector never saw.
if _measured_quiet_span_s(
readings, low_start_s, resume_idx, stop_threshold_w
) >= 60.0:
n_false_end += 1
break
@@ -1429,7 +1696,14 @@ class SuggestionEngine:
# sustain is absorbed, and the trailing dead tail is skipped - so the
# drying phase never inflates the p95 (see _resumed_low_runs).
for low_start, resume_idx in _resumed_low_runs(readings, active_thr, max_gap_s):
run = readings[resume_idx][0] - low_start
# Measure the pause the way the end gates time it, not as the
# span between "went quiet" and "came back loud" (#445). See
# _measured_quiet_span_s: the sustained-resume rule merges an
# entire burst-driven wash phase into one multi-thousand-second
# "pause" on appliances whose working power dips between bursts.
run = _measured_quiet_span_s(
readings, low_start, resume_idx, stop_threshold_w
)
if run > 0:
pause_durations.append(run)
@@ -2121,12 +2395,24 @@ class MLSuggestionEngine:
# not sustain is not a pause). ``resume_idx`` is the first active sample of
# the resume, so the tail prefix ``points[:resume_idx]`` ends in the low run.
for low_start_s, resume_idx in _resumed_low_runs(points, active_thr, max_gap_s):
dur = points[resume_idx - 1][0] - low_start_s
# Timed against stop_threshold_w like the classic heuristic (#445), so
# the ML-calibrated off_delay is fitted to the same quantity the end
# gates measure rather than to burst-phase spans.
dur, quiet_end = _measured_quiet_span(
points, low_start_s, resume_idx, stop_threshold_w
)
if dur < 30.0: # ignore motor micro-dips
continue
score: float | None = None
try:
feat = end_feat_fn(points[:resume_idx], expectation) # tail is the low run
# Score the prefix ending on the span `dur` was measured from, not
# merely the one before the resume: a low run split by readings
# between stop_threshold_w and active_thr holds several quiet
# spans, and pairing the longest span's duration with the last
# span's score is what made the `score < 0.4` filter keep the
# wrong pauses.
tail_end = resume_idx if quiet_end is None else quiet_end + 1
feat = end_feat_fn(points[:tail_end], expectation)
if feat is not None:
score = float(end_score_fn(feat))
except Exception: # pylint: disable=broad-exception-caught
@@ -280,6 +280,16 @@
"description": "The WashData device to retrain models for."
}
}
},
"mark_unloaded": {
"name": "Маркирай като разтоварено",
"description": "Потвърдете, че готовото съдържание е извадено от устройство WashData, с което се изчиства състоянието \"Чисто\" и всяко напомняне за разтоварване.",
"fields": {
"device_id": {
"name": "Устройство",
"description": "Устройството WashData, от което е извадено готовото съдържание."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Спри запис на цикъл"
},
"mark_unloaded": {
"name": "Маркирай като разтоварено"
}
}
},
@@ -280,6 +280,16 @@
"description": "WashData uređaj za ponovnu obuku modela."
}
}
},
"mark_unloaded": {
"name": "Označi kao istovareno",
"description": "Potvrdite da je gotov sadržaj izvađen iz WashData uređaja, čime se briše stanje \"Čisto\" i svaki podsjetnik za istovar.",
"fields": {
"device_id": {
"name": "Uređaj",
"description": "Uređaj WashData iz kojeg je izvađen gotov sadržaj."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Zaustavi snimanje ciklusa"
},
"mark_unloaded": {
"name": "Označi kao istovareno"
}
}
},
@@ -280,6 +280,16 @@
"description": "Zařízení WashData pro přetrénování modelů."
}
}
},
"mark_unloaded": {
"name": "Označit jako vyložené",
"description": "Potvrďte, že dokončená náplň byla vyjmuta ze zařízení WashData, čímž se zruší stav „Čistý“ i případná připomínka vyložení.",
"fields": {
"device_id": {
"name": "Zařízení",
"description": "Zařízení WashData, ze kterého byla vyjmuta dokončená náplň."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Zastavit záznam cyklu"
},
"mark_unloaded": {
"name": "Označit jako vyložené"
}
}
},
@@ -280,6 +280,16 @@
"description": "The WashData device to retrain models for."
}
}
},
"mark_unloaded": {
"name": "Markér som aflæsset",
"description": "Bekræft, at den færdige vask er taget ud af en WashData-enhed, hvilket rydder tilstanden \"Ren\" og en eventuel aflæsningspåmindelse.",
"fields": {
"device_id": {
"name": "Enhed",
"description": "WashData-enheden, hvis færdige vask er taget ud."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Stop cyklusoptagelse"
},
"mark_unloaded": {
"name": "Markér som aflæsset"
}
}
},
@@ -280,6 +280,16 @@
"description": "The WashData device to retrain models for."
}
}
},
"mark_unloaded": {
"name": "Als entladen markieren",
"description": "Bestätigen Sie, dass die fertige Ladung aus einem WashData-Gerät entnommen wurde. Damit werden der Zustand \"Sauber\" und eine eventuelle Entladeerinnerung zurückgesetzt.",
"fields": {
"device_id": {
"name": "Gerät",
"description": "Das WashData-Gerät, dessen fertige Ladung entnommen wurde."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Zyklusaufzeichnung stoppen"
},
"mark_unloaded": {
"name": "Als entladen markieren"
}
}
},
@@ -280,6 +280,16 @@
"description": "The WashData device to retrain models for."
}
}
},
"mark_unloaded": {
"name": "Σήμανση Αποφόρτωσης",
"description": "Επιβεβαιώνει ότι το ολοκληρωμένο φορτίο έχει αφαιρεθεί από μια συσκευή WashData, ακυρώνοντας την κατάσταση Καθαρός και κάθε υπενθύμιση αποφόρτωσης.",
"fields": {
"device_id": {
"name": "Συσκευή",
"description": "Η συσκευή WashData από την οποία αφαιρέθηκε το ολοκληρωμένο φορτίο."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Διακοπή εγγραφής κύκλου"
},
"mark_unloaded": {
"name": "Σήμανση Αποφόρτωσης"
}
}
},
@@ -280,6 +280,16 @@
"description": "The WashData device to retrain models for."
}
}
},
"mark_unloaded": {
"name": "Mark Unloaded",
"description": "Confirm the finished load has been taken out of a WashData device, clearing the Clean state and any unload reminder.",
"fields": {
"device_id": {
"name": "Device",
"description": "The WashData device whose finished load has been taken out."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Record Cycle Stop"
},
"mark_unloaded": {
"name": "Mark Unloaded"
}
}
},
@@ -280,6 +280,16 @@
"description": "El dispositivo WashData para el que se reentrenan los modelos."
}
}
},
"mark_unloaded": {
"name": "Marcar como descargado",
"description": "Confirmar que la carga terminada se ha sacado de un dispositivo WashData, borrando el estado Limpio y cualquier recordatorio de descarga.",
"fields": {
"device_id": {
"name": "Dispositivo",
"description": "El dispositivo WashData cuya carga terminada se ha sacado."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Detener grabación de ciclo"
},
"mark_unloaded": {
"name": "Marcar como descargado"
}
}
},
@@ -280,6 +280,16 @@
"description": "WashData seade, mille mudeleid uuesti treenida."
}
}
},
"mark_unloaded": {
"name": "Märgi mahalaadituks",
"description": "Kinnitage, et lõpetatud tsükli sisu on WashData seadmest välja võetud: olek „Puhas“ ja mahalaadimise meeldetuletus lähtestatakse.",
"fields": {
"device_id": {
"name": "Seade",
"description": "WashData seade, mille lõpetatud tsükli sisu on välja võetud."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Lõpeta tsükli salvestamine"
},
"mark_unloaded": {
"name": "Märgi mahalaadituks"
}
}
},
@@ -280,6 +280,16 @@
"description": "WashData-laite, jonka malleja koulutetaan uudelleen."
}
}
},
"mark_unloaded": {
"name": "Merkitse puretuksi",
"description": "Vahvista, että valmiin syklin sisältö on otettu pois WashData-laitteesta: Puhdas-tila ja purkamismuistutus nollataan.",
"fields": {
"device_id": {
"name": "Laite",
"description": "WashData-laite, josta valmiin syklin sisältö on otettu pois."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Lopeta syklin tallennus"
},
"mark_unloaded": {
"name": "Merkitse puretuksi"
}
}
},
@@ -280,6 +280,16 @@
"description": "L'appareil WashData pour lequel réentraîner les modèles."
}
}
},
"mark_unloaded": {
"name": "Marquer comme déchargé",
"description": "Confirmer que la charge terminée a été sortie d'un appareil WashData, ce qui efface l'état Propre et tout rappel de déchargement.",
"fields": {
"device_id": {
"name": "Appareil",
"description": "L'appareil WashData dont la charge terminée a été sortie."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Arrêter l'enregistrement du cycle"
},
"mark_unloaded": {
"name": "Marquer comme déchargé"
}
}
},
@@ -280,6 +280,16 @@
"description": "The WashData device to retrain models for."
}
}
},
"mark_unloaded": {
"name": "Označi kao istovareno",
"description": "Potvrdite da je gotov sadržaj izvađen iz WashData uređaja, čime se briše stanje \"Čist\" i svaki podsjetnik za istovar.",
"fields": {
"device_id": {
"name": "Uređaj",
"description": "Uređaj WashData iz kojeg je izvađen gotov sadržaj."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Zaustavi snimanje ciklusa"
},
"mark_unloaded": {
"name": "Označi kao istovareno"
}
}
},
@@ -280,6 +280,16 @@
"description": "The WashData device to retrain models for."
}
}
},
"mark_unloaded": {
"name": "Megjelölés kirakodottként",
"description": "Annak megerősítése, hogy a kész adagot kivették a WashData eszközből, ami törli a „Tiszta” állapotot és a kirakodási emlékeztetőt.",
"fields": {
"device_id": {
"name": "Eszköz",
"description": "Az a WashData eszköz, amelyből a kész adagot kivették."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Ciklusrögzítés leállítása"
},
"mark_unloaded": {
"name": "Megjelölés kirakodottként"
}
}
},
@@ -280,6 +280,16 @@
"description": "WashData tækið til að endurþjálfa líkön fyrir."
}
}
},
"mark_unloaded": {
"name": "Merkja sem losað",
"description": "Staðfestu að búið sé að taka fullunna þvottinn úr WashData tæki, sem hreinsar stöðuna \"Hreint\" og hvers kyns losunaráminningu.",
"fields": {
"device_id": {
"name": "Tæki",
"description": "WashData tækið sem búið er að taka fullunna þvottinn úr."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Stöðva upptöku hringrásar"
},
"mark_unloaded": {
"name": "Merkja sem losað"
}
}
},
@@ -280,6 +280,16 @@
"description": "Il dispositivo WashData di cui riaddestrare i modelli."
}
}
},
"mark_unloaded": {
"name": "Segna come scaricato",
"description": "Confermare che il carico terminato è stato tolto da un dispositivo WashData, azzerando lo stato Pulito e ogni promemoria di scarico.",
"fields": {
"device_id": {
"name": "Dispositivo",
"description": "Il dispositivo WashData da cui è stato tolto il carico terminato."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Interrompi registrazione ciclo"
},
"mark_unloaded": {
"name": "Segna come scaricato"
}
}
},
@@ -280,6 +280,16 @@
"description": "モデルを再トレーニングする WashData デバイス。"
}
}
},
"mark_unloaded": {
"name": "取り出し済みとしてマーク",
"description": "WashData デバイスから終了した中身を取り出したことを確認し、クリーン状態と取り出しリマインダーを解除します。",
"fields": {
"device_id": {
"name": "デバイス",
"description": "終了した中身を取り出した WashData デバイス。"
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "サイクル記録を停止"
},
"mark_unloaded": {
"name": "取り出し済みとしてマーク"
}
}
},
@@ -280,6 +280,16 @@
"description": "The WashData device to retrain models for."
}
}
},
"mark_unloaded": {
"name": "꺼냄으로 표시",
"description": "WashData 장치에서 완료된 내용물을 꺼냈음을 확인하여 깨끗한 상태와 모든 꺼내기 알림을 해제합니다.",
"fields": {
"device_id": {
"name": "장치",
"description": "완료된 내용물을 꺼낸 WashData 장치입니다."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "사이클 기록 중지"
},
"mark_unloaded": {
"name": "꺼냄으로 표시"
}
}
},
@@ -280,6 +280,16 @@
"description": "WashData įrenginys, kurio modelius reikia iš naujo apmokyti."
}
}
},
"mark_unloaded": {
"name": "Pažymėti kaip iškrautą",
"description": "Patvirtinkite, kad baigto ciklo turinys išimtas iš „WashData“ įrenginio: būsena „Švarus“ ir iškrovimo priminimas bus panaikinti.",
"fields": {
"device_id": {
"name": "Įrenginys",
"description": "„WashData“ įrenginys, iš kurio išimtas baigto ciklo turinys."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Sustabdyti ciklo įrašymą"
},
"mark_unloaded": {
"name": "Pažymėti kaip iškrautą"
}
}
},
@@ -280,6 +280,16 @@
"description": "WashData ierīce, kurai pārapmācīt modeļus."
}
}
},
"mark_unloaded": {
"name": "Atzīmēt kā izkrautu",
"description": "Apstipriniet, ka pabeigtā cikla saturs ir izņemts no WashData ierīces: stāvoklis „Tīrs“ un izkraušanas atgādinājums tiks notīrīti.",
"fields": {
"device_id": {
"name": "Ierīce",
"description": "WashData ierīce, no kuras ir izņemts pabeigtā cikla saturs."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Beigt cikla ierakstīšanu"
},
"mark_unloaded": {
"name": "Atzīmēt kā izkrautu"
}
}
},
@@ -280,6 +280,16 @@
"description": "WashData уредот за повторна обука на модели."
}
}
},
"mark_unloaded": {
"name": "Означи како истоварено",
"description": "Потврдете дека завршената содржина е извадена од уредот WashData, со што се брише состојбата \"Чиста\" и секој потсетник за истовар.",
"fields": {
"device_id": {
"name": "Уред",
"description": "Уредот WashData од кој е извадена завршената содржина."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Запри снимање на циклус"
},
"mark_unloaded": {
"name": "Означи како истоварено"
}
}
},
@@ -280,6 +280,16 @@
"description": "WashData-enheten det skal trenes modeller for."
}
}
},
"mark_unloaded": {
"name": "Merk som utlastet",
"description": "Bekreft at den ferdige vasken er tatt ut av en WashData-enhet, noe som fjerner tilstanden \"Rengjøre\" og en eventuell utlastingspåminnelse.",
"fields": {
"device_id": {
"name": "Enhet",
"description": "WashData-enheten som har fått den ferdige vasken tatt ut."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Stopp syklusopptak"
},
"mark_unloaded": {
"name": "Merk som utlastet"
}
}
},
@@ -280,6 +280,16 @@
"description": "Het WashData-apparaat waarvoor modellen hertraind moeten worden."
}
}
},
"mark_unloaded": {
"name": "Markeren als uitgeladen",
"description": "Bevestig dat de voltooide lading uit een WashData-apparaat is gehaald, waarmee de staat \"Schoon\" en een eventuele uitlaadherinnering worden gewist.",
"fields": {
"device_id": {
"name": "Apparaat",
"description": "Het WashData-apparaat waarvan de voltooide lading is uitgehaald."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Stop cyclusopname"
},
"mark_unloaded": {
"name": "Markeren als uitgeladen"
}
}
},
@@ -60,6 +60,7 @@
"fair_fit_tip": "Умерена последователност на съвпадението – някои цикли, присвоени на този профил, имат по-ниски резултати за доверие. Маркирайте повече цикли или запишете отново профила, за да подобрите точността.",
"feedback_requested": "Иска се обратна връзка",
"golden_cycle": "Записан референтен цикъл",
"duration_outlier": "Цикли с необичайна продължителност: {n}",
"improving": "↗ подобряване",
"manual": "(ръчно избран)",
"needs_review": "Има нужда от преглед",
@@ -256,7 +257,15 @@
"hist_scan": "Търсене на цикли",
"hist_import_n": "Импортиране на цикли ({n})",
"hist_goto_cycles": "Покажи циклите",
"save_total_cycles": "Запазване на общия брой"
"save_total_cycles": "Запазване на общия брой",
"clear": "Изчистване",
"add_brand": "Добавете марка",
"add_appliance": "Добавете уред"
},
"note": {
"off_delay": {
"effective": "Завършен цикъл реално изчаква {eff} с, а не {delay} с: Минималната пауза при изключване ({gap} с) е по-дълга и има предимство."
}
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +387,8 @@
"community_store": "Магазин на общността",
"online_account": "Магазин на общността и онлайн функции",
"import_power_history": "Импортиране на историята на мощността",
"service_status": "Статус на сервиза"
"service_status": "Статус на сервиза",
"online_account_tab": "Онлайн и общност"
},
"health": {
"fair": "Приемливо качество на съвпадение",
@@ -519,7 +529,8 @@
"pg_tile_matched_tip": "Цикли, правилно отнесени към позната програма",
"pg_time_left": "Оставащо време",
"pg_weak_match": "Слабо съвпадение",
"phase_name": "Име",
"phase_name": "Име на фаза",
"name": "Име",
"phase_timeline": "Времева линия на фазите",
"power": "Мощност",
"profile": "Профил",
@@ -1032,6 +1043,9 @@
"imported_readonly": "Импортирано от магазина на общността. Показва се за справка и съпоставяне. Не се брои във вашата статистика и не може да се редактира.",
"merge_no_imports": "Импортираните записи не могат да се обединят с реален цикъл. Премахнете избора им, за да обедините.",
"store_enable_hint": "Активирайте онлайн функциите в настройките, за да разглеждате и импортирате еталонни цикли на общността.",
"advisory_duration_outlier": "Цикли, означени като '{name}' ({n}), са твърде далеч от обичайната ѝ продължителност, за да бъдат някога съпоставени с нея (най-голямо отклонение: {ratio}x). Вероятно са означени погрешно или едно записване е обхванало две изпълнения. Преозначете ги или ги разделете, за да спрат да изкривяват оценката на времето за тази програма.",
"standby_above_stop_title": "Все още консумира {idle} Вт при приключване на цикъла",
"standby_above_stop_sub": "{n} от последните {total} цикъла. Закъснението при изключване започва да тече едва когато мощността падне под Прага на спиране ({stop} Вт), така че циклите завършват със закъснение или трябва да се спират ръчно.",
"online_intro": "Споделяйте и изтегляйте еталонни цикли с други потребители на WashData. Всички онлайн функции са по избор и по подразбиране са изключени.",
"store_share_intro": "Качете този еталонен цикъл, за да могат да го използват други с този уред. Той се преглежда, преди да стане публичен.",
"online_intro_global": "Разглеждайте и споделяйте еталонни записи с други потребители на WashData и потвърждавайте записите за уреди. Една връзка важи за цялата ви интеграция WashData; марката и моделът на уреда се задават за всеки уред в раздела „Основни“. Всички онлайн функции са по избор и по подразбиране са изключени.",
@@ -1087,7 +1101,9 @@
"odometer_intro": "Този общ брой само расте. Изтриването на запис за цикъл не го променя и той продължава да се увеличава и след ограничението за съхранявана история, така че напомнянията за сервиз остават точни.",
"odometer_correct_hint": "Задайте това, ако е записан цикъл, който никога не се е изпълнявал, или ако уредът вече е изпълнявал цикли преди инсталирането на WashData.",
"no_reminders_set": "Все още няма зададени напомняния за сервиз.",
"toast_program_armed": "Програмата е зададена за следващия цикъл: {program}"
"toast_program_armed": "Програмата е зададена за следващия цикъл: {program}",
"toast_range_required": "Задайте валиден диапазон От/До",
"no_profiles_option": "(няма профили, добавете нов)"
},
"phase_desc": {
"anti_crease": "Случайни кратки движения след завършване за намаляване на бръчките.",
@@ -1410,8 +1426,8 @@
"label": "Закъснение на напомнянето за разтоварване"
},
"notify_unload_repeat": {
"doc": "Продължава да изпраща напомнянето за разтоварване на всеки \"Закъснение на напомнянето за разтоварване\" минути, докато не отворите вратата или не докоснете \"Спри напомнянията\" в известието. Изисква Обект на сензор за врата (както и самото напомняне). Бутонът за отхвърляне работи в известията на мобилното приложение на Home Assistant (companion).",
"label": "Повтаряне до отваряне на вратата"
"doc": "Продължава да изпраща напомнянето за разтоварване на всеки \"Закъснение на напомнянето за разтоварване\" минути, докато разтоварването не бъде потвърдено или не докоснете \"Спри напомнянията\" в известието. Необходим е начин за потвърждаване на разтоварването: Обект на сензор за врата, Обект за потвърждение на разтоварване или опцията \"Ръчно потвърждаване на разтоварването\" по-долу. Бутонът за отхвърляне работи в известията на мобилното приложение на Home Assistant (companion).",
"label": "Повтаряне до разтоварване"
},
"off_delay": {
"doc": "Време за изчакване след падане на захранването, преди да се обяви цикълът за завършен. Ако захранването се възобнови в рамките на този прозорец, цикълът продължава безпроблемно - това свързва паузите между етапите на пране. Съдомиялните машини имат дълги фази на сушене (изключени за 20-60 минути), така че времето за изключване трябва да надвишава това, за да поддържа цялото измиване+сушене като един цикъл.",
@@ -1623,6 +1639,18 @@
"curve_preroll_seconds": {
"doc": "Колко секунди отчитания от прекъснати опити за стартиране WashData може да пренесе в началото на кривата на цикъла. Кривата на цикъла обикновено започва от опита за стартиране, който в крайна сметка е успял, затова при машина, която опитва няколко пъти, преди да потегли - избор на програма, заключване на вратата, първо пълнене - в началото на всяка крива липсват първите минути реална работа. Задайте стойност малко над най-дългия такъв разбег на вашата машина (300 с стигат с излишък за повечето). 0 изключва функцията. Имайте предвид, че включването ѝ измества записаното начало по-рано, така че циклите, записани преди и след промяната, носят различна продължителност за една и съща програма; научените средни стойности ще се изместват, докато по-старите цикли не отпаднат от историята.",
"label": "Предзапис на кривата"
},
"unload_confirm_entity": {
"label": "Обект за потвърждение на разтоварване",
"doc": "Незадължителен обект, чието активиране означава, че съдържанието е извадено, за машини, които не могат да имат сензор на вратата. Работи всякакъв вид: бутон Zigbee или NFC, помощник input_button, сензор за движение пред машината, сцена. Всяка промяна на състоянието се брои за потвърждение, с изключение на изключване и преминаване в неналично състояние. Задаването на това включва напомнянето за разтоварване и на устройство без Обект на сензор за врата."
},
"unload_track_without_door": {
"label": "Ръчно потвърждаване на разтоварването",
"doc": "Изпълнявайте напомнянето за разтоварване на устройство без Обект на сензор за врата и без Обект за потвърждение на разтоварване, а разтоварването потвърждавайте от собствена автоматизация: натиснете бутонния обект \"Маркирай като разтоварено\" или извикайте услугата ha_washdata.mark_unloaded. Игнорира се, когато е зададен Обект на сензор за врата, който вече дава този сигнал."
},
"notify_icon_color": {
"label": "Цвят на известие",
"doc": "Акцентен цвят за известията на този уред, за да се различават пералнята, сушилнята и съдомиялната машина от пръв поглед. iOS оцветява иконата и лентата за напредък на Live Activity на заключения екран и в Dynamic Island. Android получава същия цвят, но какво прави с него зависи от телефона: някои оцветяват иконата, а много от тях (особено Samsung One UI) го пренебрегват напълно. И двете платформи разбират Hex (напр. #03A9F4). Оставете празно за цвета по подразбиране на платформата."
}
},
"setting_group": {
@@ -1716,7 +1744,7 @@
"all_statuses": "Всички статуси",
"ambiguous": "Двусмислен",
"auto_detect": "Автоматично откриване",
"clear": "ясно",
"clear": "Недвусмислен",
"completed": "Завършено",
"fine_tuning": "фина настройка сега...",
"force_stopped": "Принудително спряно",
@@ -1854,6 +1882,7 @@
"store_no_appliance": "Първо задайте марката и модела на уреда в настройките.",
"store_share_failed": "Споделянето е неуспешно: {error}",
"store_shared": "Споделено в магазина на общността, изчаква преглед.",
"contrib_device_changed": "Добавено в магазина на общността, но избраният уред се смени, докато формулярът беше отворен. Изберете го в настройките.",
"appliance_added": "Уредът е добавен, изчаква одобрение",
"thanks_confirming": "Благодарим за потвърждението",
"device_approved": "Одобрено от общността",
@@ -1878,7 +1907,9 @@
"unlabel_done": "Премахнат етикет от цикли: {count}",
"unlabel_failed": "Премахването на етикета е неуспешно: {error}",
"odometer_saved": "Общият брой цикли е запазен",
"odometer_save_failed": "Общият брой не може да бъде запазен: {error}"
"odometer_save_failed": "Общият брой не може да бъде запазен: {error}",
"store_pick_profile_share": "Изберете профил, в който да споделите",
"remove_failed": "Премахването е неуспешно: {error}"
},
"suggestion": {
"both_agree": "WashData препоръчва",
@@ -1978,6 +2009,7 @@
"summary": "Изключени са {total} грешно открити цикъла: {parts}.",
"reason": {
"force_stopped": "принудително спрени",
"user_stopped": "ръчно спрени",
"interrupted": "прекъснати",
"incomplete": "незавършени",
"noise": "шум",
@@ -2024,7 +2056,7 @@
"duration_weight": "Етап 4: колко силно съответствието на продължителността влияе върху крайния резултат (по подразбиране 0,22)",
"energy_weight": "Етап 4: колко силно съответствието на енергията влияе върху крайния резултат (по подразбиране 0,22)",
"profile_match_min_duration_ratio": "Етап 1: минимална дължина на цикъла като дял от продължителността на профила; по подразбиране 0,1 (10%)",
"profile_match_max_duration_ratio": "Етап 1: максимална дължина на цикъла като дял от продължителността на профила; по подразбиране 1,5 (150%)",
"profile_match_max_duration_ratio": "Етап 1: максимална дължина на цикъла като дял от продължителността на профила; по подразбиране 1,8 (180%)",
"keep_min_score": "Етап 2: минимален резултат за оставане в надпреварата; по подразбиране 0,1 допуска слаби съвпадения, по-късните етапи избират най-доброто",
"dtw_blend": "Етап 3: 0 = само основен резултат, 1 = само DTW резултат, 0,5 = равно смесване (по подразбиране)",
"dtw_ensemble_w": "Етап 3 (режим на ансамбъл): тежест на мащабиран L1 срещу производен DTW; по подразбиране 0,7 дава предимство на нивото",
@@ -60,6 +60,7 @@
"fair_fit_tip": "Umjerena konzistentnost podudaranja – neki ciklusi dodijeljeni ovom profilu imaju niže rezultate pouzdanosti. Označite više ciklusa ili ponovo snimite profil da biste poboljšali preciznost.",
"feedback_requested": "Zatražena je povratna informacija",
"golden_cycle": "Snimljeni referentni ciklus",
"duration_outlier": "Ciklusi neobične dužine: {n}",
"improving": "↗ poboljšanje",
"manual": "(ručno odabrano)",
"needs_review": "Potreban pregled",
@@ -256,7 +257,15 @@
"hist_scan": "Potraži cikluse",
"hist_import_n": "Uvezi cikluse: {n}",
"hist_goto_cycles": "Prikaži cikluse",
"save_total_cycles": "Sačuvaj ukupan broj"
"save_total_cycles": "Sačuvaj ukupan broj",
"clear": "Očisti",
"add_brand": "Dodaj marku",
"add_appliance": "Dodaj uređaj"
},
"note": {
"off_delay": {
"effective": "Završen ciklus zapravo čeka {eff} s, a ne {delay} s: Min razmak isključivanja ({gap} s) je duži i ima prednost."
}
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +387,8 @@
"community_store": "Zajednička trgovina",
"online_account": "Zajednička trgovina i online funkcije",
"import_power_history": "Uvoz historije snage",
"service_status": "Status servisa"
"service_status": "Status servisa",
"online_account_tab": "Online i zajednica"
},
"health": {
"fair": "Prihvatljiva kvaliteta podudaranja",
@@ -519,7 +529,8 @@
"pg_tile_matched_tip": "Ciklusi ispravno pripisani poznatom programu",
"pg_time_left": "Preostalo vrijeme",
"pg_weak_match": "Slabo podudaranje",
"phase_name": "Ime",
"phase_name": "Ime faze",
"name": "Ime",
"phase_timeline": "Vremenska linija faza",
"power": "Snaga",
"profile": "Profil",
@@ -1032,6 +1043,9 @@
"imported_readonly": "Uvezeno iz zajedničke trgovine. Prikazano za referencu i podudaranje. Ne broji se u vašu statistiku i ne može se uređivati.",
"merge_no_imports": "Uvezene snimke ne mogu se spojiti sa stvarnim ciklusom. Poništite njihov odabir da biste ih spojili.",
"store_enable_hint": "Omogućite online funkcije u Postavkama da biste mogli pregledavati i uvoziti referentne cikluse zajednice.",
"advisory_duration_outlier": "Ciklusi označeni kao '{name}' ({n}) previše odstupaju od njegove uobičajene dužine da bi se ikada mogli podudariti s njim (najveće odstupanje: {ratio}x). Vjerovatno su pogrešno označeni ili je jedno snimanje obuhvatilo dva pokretanja. Ponovo ih označite ili ih podijelite kako bi prestali iskrivljavati procjenu vremena za ovaj program.",
"standby_above_stop_title": "Još uvijek povlači {idle} W kada je ciklus završio",
"standby_above_stop_sub": "{n} od posljednjih {total} ciklusa. Kašnjenje isključivanja počinje tek kada snaga padne ispod Praga zaustavljanja ({stop} W), pa se ciklusi završavaju sa zakašnjenjem ili ih treba zaustaviti ručno.",
"online_intro": "Dijelite i preuzimajte referentne cikluse s drugim korisnicima WashData. Sve online funkcije su dobrovoljne i podrazumijevano isključene.",
"store_share_intro": "Otpremite ovaj referentni ciklus kako bi ga mogli koristiti drugi s istim uređajem. Pregleda se prije nego što postane javno dostupan.",
"online_intro_global": "Pregledavajte i dijelite referentne snimke s drugim korisnicima WashData te potvrđujte unose uređaja. Jedna veza vrijedi za cijelu vašu WashData integraciju; marka i model uređaja postavljaju se za svaki uređaj u odjeljku Osnovno. Sve online funkcije su dobrovoljne i podrazumijevano isključene.",
@@ -1087,7 +1101,9 @@
"odometer_intro": "Ovaj ukupan broj samo raste. Brisanje zapisa o ciklusu ga ne mijenja i nastavlja se povećavati i nakon ograničenja sačuvane historije, tako da podsjetnici za servis ostaju tačni.",
"odometer_correct_hint": "Postavite ovo ako je WashData zabilježio ciklus koji se nikada nije dogodio ili ako je uređaj već obavio cikluse prije instaliranja WashData.",
"no_reminders_set": "Još nema postavljenih podsjetnika za servis.",
"toast_program_armed": "Program postavljen za sljedeći ciklus: {program}"
"toast_program_armed": "Program postavljen za sljedeći ciklus: {program}",
"toast_range_required": "Postavite ispravan raspon Od/Do",
"no_profiles_option": "(nema profila, dodajte novi)"
},
"phase_desc": {
"anti_crease": "Povremena kratka spuštanja nakon završetka za smanjenje bora.",
@@ -1414,8 +1430,8 @@
"label": "Kašnjenje podsjetnika za istovar"
},
"notify_unload_repeat": {
"doc": "Ponovo šalje podsjetnik za istovar svakih \"Kašnjenje podsjetnika za istovar\" minuta dok ne otvorite vrata ili ne dodirnete \"Prestani podsjećati\" na obavijesti. Zahtijeva Entitet senzora vrata (kao i sam podsjetnik). Dugme za odbacivanje radi na obavijestima mobilne aplikacije Home Assistant (companion app).",
"label": "Ponavljaj dok se vrata ne otvore"
"doc": "Ponovo šalje podsjetnik za istovar svakih \"Kašnjenje podsjetnika za istovar\" minuta dok se istovar ne potvrdi ili dok ne dodirnete \"Prestani podsjećati\" na obavijesti. Potreban je način za potvrdu istovara: Entitet senzora vrata, Entitet potvrde istovara ili opcija \"Ručno potvrdi istovar\" ispod. Dugme za odbacivanje radi na obavijestima mobilne aplikacije Home Assistant (companion app).",
"label": "Ponavljaj do istovara"
},
"off_delay": {
"doc": "Vrijeme čekanja nakon pada struje prije nego što se ciklus proglasi završenim. Ako se napajanje nastavi unutar ovog prozora, ciklus se nastavlja neometano - ovo premošćuje pauze između faza pranja. Mašine za pranje posuđa imaju duge faze sušenja (isključenje na 20-60 min) tako da kašnjenje isključenja mora premašiti to da bi se cijelo pranje+sušilo kao jedan ciklus.",
@@ -1623,6 +1639,18 @@
"energy_price_dynamic": {
"label": "Vremenski ponderisan trošak",
"doc": "Naplaćuje svaki ciklus po cijeni koja je važila u svakom trenutku njegovog rada, umjesto po jednoj cijeni koja je važila kada je završio. Odnosi se samo na entitet cijene (statična cijena se ne može mijenjati). Ne treba dodatno podešavanje: cijena se prati uživo dok ciklus traje, a Obrada historije u Dijagnostici ponovo obračunava starije cikluse iz recordera. Isključeno = klasično ponašanje, jedna cijena zamrznuta na kraju."
},
"unload_confirm_entity": {
"label": "Entitet potvrde istovara",
"doc": "Opcioni entitet čije aktiviranje znači da je sadržaj izvađen, za mašine koje ne mogu imati senzor vrata. Radi bilo koja vrsta: Zigbee ili NFC dugme, input_button pomoćnik, senzor pokreta ispred mašine, scena. Svaka promjena stanja broji se kao potvrda, osim isključivanja i prelaska u nedostupno stanje. Postavljanje ovoga ujedno omogućava podsjetnik za istovar na uređaju bez Entiteta senzora vrata."
},
"unload_track_without_door": {
"label": "Ručno potvrdi istovar",
"doc": "Pokrenite podsjetnik za istovar na uređaju bez Entiteta senzora vrata i bez Entiteta potvrde istovara, a istovar potvrdite iz vlastite automatizacije: pritisnite entitet dugmeta \"Označi kao istovareno\" ili pozovite uslugu ha_washdata.mark_unloaded. Zanemaruje se kada je postavljen Entitet senzora vrata, koji već daje taj signal."
},
"notify_icon_color": {
"label": "Boja obavijesti",
"doc": "Akcentna boja obavještenja ovog uređaja, kako bi se mašina za pranje rublja, sušilica i mašina za pranje posuđa razlikovale na prvi pogled. iOS boji ikonu i traku napretka Live Activity na zaključanom ekranu i u Dynamic Island. Android dobiva istu boju, ali šta će s njom uraditi ovisi o telefonu: neki oboje ikonu, a mnogi (posebno Samsung One UI) je potpuno zanemaruju. Obje platforme razumiju Hex (npr. #03A9F4). Ostavite prazno za zadanu boju platforme."
}
},
"setting_group": {
@@ -1716,7 +1744,7 @@
"all_statuses": "Svi statusi",
"ambiguous": "Dvosmisleno",
"auto_detect": "Automatsko otkrivanje",
"clear": "Jasno",
"clear": "Jednoznačno",
"completed": "Završeno",
"fine_tuning": "sada fino podešavanje…",
"force_stopped": "Prisilno zaustavljeno",
@@ -1835,6 +1863,7 @@
"summary": "Isključeno {total} pogrešno otkrivenih ciklusa: {parts}.",
"reason": {
"force_stopped": "prisilno zaustavljeni",
"user_stopped": "ručno zaustavljeni",
"interrupted": "prekinuti",
"incomplete": "nepotpuni",
"noise": "buka",
@@ -1965,6 +1994,7 @@
"store_no_appliance": "Prvo postavite marku i model uređaja u Postavkama.",
"store_share_failed": "Dijeljenje nije uspjelo: {error}",
"store_shared": "Podijeljeno u zajedničku trgovinu, čeka pregled.",
"contrib_device_changed": "Dodano u zajedničku trgovinu, ali se odabrani uređaj promijenio dok je obrazac bio otvoren. Odaberite ga u Postavkama.",
"appliance_added": "Uređaj dodan, čeka odobrenje",
"thanks_confirming": "Hvala na potvrdi",
"device_approved": "Odobreno od strane zajednice",
@@ -1989,7 +2019,9 @@
"unlabel_done": "Uklonjena oznaka s ciklusa: {count}",
"unlabel_failed": "Uklanjanje oznake nije uspjelo: {error}",
"odometer_saved": "Ukupan broj ciklusa je sačuvan",
"odometer_save_failed": "Nije moguće sačuvati ukupan broj: {error}"
"odometer_save_failed": "Nije moguće sačuvati ukupan broj: {error}",
"store_pick_profile_share": "Odaberite profil za dijeljenje",
"remove_failed": "Uklanjanje nije uspjelo: {error}"
},
"trend": {
"down": "Opadajući trend",
@@ -2024,7 +2056,7 @@
"duration_weight": "Faza 4: koliko jako slaganje trajanja utječe na konačnu ocjenu (zadano 0,22)",
"energy_weight": "Faza 4: koliko jako slaganje energije utječe na konačnu ocjenu (zadano 0,22)",
"profile_match_min_duration_ratio": "Faza 1: minimalna dužina ciklusa kao udio trajanja profila; zadano 0,1 (10%)",
"profile_match_max_duration_ratio": "Faza 1: maksimalna dužina ciklusa kao udio trajanja profila; zadano 1,5 (150%)",
"profile_match_max_duration_ratio": "Faza 1: maksimalna dužina ciklusa kao udio trajanja profila; zadano 1,8 (180%)",
"keep_min_score": "Faza 2: minimalna ocjena za ostanak u utrci; zadano 0,1 dopušta slabe podudarnosti, kasniji koraci biraju najbolje",
"dtw_blend": "Faza 3: 0 = samo osnovna ocjena, 1 = samo DTW ocjena, 0,5 = jednako miješanje (zadano)",
"dtw_ensemble_w": "Faza 3 (kombinirani način): težina skaliranog L1 nasuprot derivatnog DTW; zadano 0,7 favorizira nivo",
@@ -60,6 +60,7 @@
"fair_fit_tip": "Střední konzistence shody – některé cykly přiřazené k tomuto profilu mají nižší skóre spolehlivosti. Označte více cyklů nebo znovu zaznamenejte profil, abyste zvýšili přesnost.",
"feedback_requested": "Vyžádána zpětná vazba",
"golden_cycle": "Zaznamenaný referenční cyklus",
"duration_outlier": "Cykly s neobvyklou délkou: {n}",
"improving": "↗ zlepšování",
"manual": "(vybráno ručně)",
"needs_review": "Vyžaduje revizi",
@@ -256,7 +257,15 @@
"hist_scan": "Vyhledat cykly",
"hist_import_n": "Importovat cykly: {n}",
"hist_goto_cycles": "Zobrazit cykly",
"save_total_cycles": "Uložit celkový počet"
"save_total_cycles": "Uložit celkový počet",
"clear": "Vymazat",
"add_brand": "Přidat značku",
"add_appliance": "Přidat spotřebič"
},
"note": {
"off_delay": {
"effective": "Dokončený cyklus ve skutečnosti čeká {eff} s, ne {delay} s: Min. mezera vypnutí ({gap} s) je delší a má přednost."
}
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +387,8 @@
"community_store": "Komunitní obchod",
"online_account": "Komunitní obchod a online funkce",
"import_power_history": "Import historie výkonu",
"service_status": "Stav servisu"
"service_status": "Stav servisu",
"online_account_tab": "Online a komunita"
},
"health": {
"fair": "Přijatelná shoda",
@@ -519,7 +529,8 @@
"pg_tile_matched_tip": "Cykly správně přiřazené ke známému programu",
"pg_time_left": "Zbývající čas",
"pg_weak_match": "Slabá shoda",
"phase_name": "Jméno",
"phase_name": "Název fáze",
"name": "Jméno",
"phase_timeline": "Časová osa fází",
"power": "Výkon",
"profile": "Profil",
@@ -1032,6 +1043,9 @@
"imported_readonly": "Importováno z komunitního obchodu. Zobrazuje se pro referenci a porovnávání. Nezapočítává se do vašich statistik a nelze jej upravovat.",
"merge_no_imports": "Importované záznamy nelze sloučit se skutečným cyklem. Zrušte jejich výběr pro sloučení.",
"store_enable_hint": "Povolte online funkce v Nastavení, abyste mohli procházet a importovat referenční cykly komunity.",
"advisory_duration_outlier": "Cykly označené jako '{name}' ({n}) jsou příliš daleko od jeho obvyklé délky, než aby se s ním kdy mohly shodovat (nejhorší: {ratio}x). Pravděpodobně jsou špatně označené, nebo jeden záznam zachytil dva běhy. Označte je znovu nebo je rozdělte, aby přestaly zkreslovat odhad času tohoto programu.",
"standby_above_stop_title": "Při ukončení cyklu stále odebírá {idle} W",
"standby_above_stop_sub": "{n} z posledních {total} cyklů. Zpoždění vypnutí se začne počítat až ve chvíli, kdy příkon klesne pod Práh zastavení ({stop} W), takže cykly končí pozdě nebo je nutné je zastavit ručně.",
"online_intro": "Sdílejte a stahujte referenční cykly s ostatními uživateli WashData. Všechny online funkce jsou dobrovolné a ve výchozím nastavení vypnuté.",
"store_share_intro": "Nahrajte tento referenční cyklus, aby jej mohli používat ostatní se stejným spotřebičem. Před zveřejněním bude zkontrolován.",
"online_intro_global": "Procházejte a sdílejte referenční záznamy s ostatními uživateli WashData a potvrzujte záznamy spotřebičů. Jedno připojení platí pro celou vaši integraci WashData; značka a model spotřebiče se nastavují pro každé zařízení v sekci Základní. Všechny online funkce jsou dobrovolné a ve výchozím nastavení vypnuté.",
@@ -1087,7 +1101,9 @@
"odometer_intro": "Tento celkový počet pouze roste. Smazání záznamu o cyklu jej nezmění a počítá dál i za limitem uložené historie, takže připomínky servisu zůstávají správné.",
"odometer_correct_hint": "Nastavte, pokud WashData zaznamenala cyklus, který nikdy neproběhl, nebo pokud spotřebič už absolvoval cykly před instalací WashData.",
"no_reminders_set": "Zatím nejsou nastaveny žádné připomínky servisu.",
"toast_program_armed": "Program nastaven pro další cyklus: {program}"
"toast_program_armed": "Program nastaven pro další cyklus: {program}",
"toast_range_required": "Nastavte platný rozsah Od/Do",
"no_profiles_option": "(zatím žádné profily, přidejte nový)"
},
"phase_desc": {
"anti_crease": "Občasné krátké otáčení bubnu po dokončení cyklu pro prevenci mačkání.",
@@ -1410,8 +1426,8 @@
"label": "Zpoždění připomínky vyložení"
},
"notify_unload_repeat": {
"doc": "Znovu odesílá připomínku vyložení každých \"Zpoždění připomínky vyložení\" minut, dokud neotevřete dveře nebo v oznámení neklepnete na \"Přestat připomínat\". Vyžaduje Entitu senzoru dveří (stejně jako samotná připomínka). Tlačítko pro zrušení funguje u oznámení mobilní aplikace Home Assistant (companion app).",
"label": "Opakovat, dokud se neotevřou dveře"
"doc": "Znovu odesílá připomínku vyložení každých „Zpoždění připomínky vyložení“ minut, dokud není vyložení potvrzeno nebo v oznámení neklepnete na „Přestat připomínat“. Vyžaduje způsob, jak vyložení potvrdit: Entitu senzoru dveří, Entitu potvrzení vyložení nebo níže uvedenou možnost „Ruční potvrzení vyložení“. Tlačítko pro zrušení funguje u oznámení mobilní aplikace Home Assistant (companion app).",
"label": "Opakovat do vyložení"
},
"off_delay": {
"doc": "Čas čekání po poklesu příkonu před vyhlášením ukončení cyklu. Pokud se příkon obnoví v tomto okně, cyklus plynule pokračuje – to přemostí pauzy mezi fázemi praní. Myčky nádobí mají dlouhé fáze sušení (příkon vypnut po dobu 20–60 minut), takže zpozdění vypnutí musí tuto dobu překročit, aby byl celkový cyklus mytí+sušení zachován jako jeden.",
@@ -1623,6 +1639,18 @@
"curve_preroll_seconds": {
"doc": "Kolik sekund odečtů z neúspěšných pokusů o spuštění může WashData přenést na začátek křivky cyklu. Křivka cyklu obvykle začíná u pokusu o spuštění, který nakonec uspěl, takže u stroje, který to před rozběhnutím několikrát zkusí - volba programu, zámek dveří, první napouštění - chybí v každé křivce první minuty skutečné činnosti. Nastavte tuto hodnotu o něco výše, než je nejdelší takový rozběh vašeho stroje (300 s většině bohatě stačí). 0 funkci vypne. Mějte na paměti, že zapnutí posune zaznamenaný začátek dříve, takže cykly zaznamenané před změnou a po ní nesou pro stejný program různé doby trvání; naučené průměry se budou posouvat, dokud starší cykly nevypadnou z historie.",
"label": "Náběh křivky"
},
"unload_confirm_entity": {
"label": "Entita potvrzení vyložení",
"doc": "Volitelná entita, jejíž aktivace znamená, že náplň byla vyjmuta, pro spotřebiče, na které nelze umístit senzor dveří. Funguje jakýkoli typ: tlačítko Zigbee nebo NFC, pomocník input_button, senzor pohybu před spotřebičem, scéna. Za potvrzení se počítá každá změna stavu kromě vypnutí a přechodu do stavu nedostupný. Nastavení této možnosti také zapne připomínku vyložení na zařízení bez Entity senzoru dveří."
},
"unload_track_without_door": {
"label": "Ruční potvrzení vyložení",
"doc": "Spustí připomínku vyložení na zařízení bez Entity senzoru dveří a bez Entity potvrzení vyložení, přičemž vyložení potvrdíte z vlastní automatizace: stiskněte entitu tlačítka „Označit jako vyložené“ nebo zavolejte službu ha_washdata.mark_unloaded. Ignorováno, pokud je nastavena Entita senzoru dveří, která tento signál již poskytuje."
},
"notify_icon_color": {
"label": "Barva oznámení",
"doc": "Barva zvýraznění oznámení tohoto spotřebiče, aby se pračka, sušička a myčka nádobí daly na první pohled rozeznat. iOS obarví ikonu a ukazatel průběhu Live Activity na zamčené obrazovce a v Dynamic Island. Android dostane stejnou barvu, ale co s ní udělá, záleží na telefonu: některé obarví ikonu a mnohé (zejména Samsung One UI) ji zcela ignorují. Obě platformy rozumí formátu Hex (např. #03A9F4). Nechte prázdné pro výchozí barvu platformy."
}
},
"setting_group": {
@@ -1716,7 +1744,7 @@
"all_statuses": "Všechny stavy",
"ambiguous": "Dvojznačný",
"auto_detect": "Automatická detekce",
"clear": "Čistý",
"clear": "Jednoznačný",
"completed": "Dokončeno",
"fine_tuning": "teď dolaďuji…",
"force_stopped": "Vynuceně zastaveno",
@@ -1835,6 +1863,7 @@
"summary": "Vyloučeno {total} chybně detekovaných cyklů: {parts}.",
"reason": {
"force_stopped": "vynuceně zastavené",
"user_stopped": "ručně zastavené",
"interrupted": "přerušené",
"incomplete": "neúplné",
"noise": "hluk",
@@ -1965,6 +1994,7 @@
"store_no_appliance": "Nejprve nastavte značku a model spotřebiče v Nastavení.",
"store_share_failed": "Sdílení selhalo: {error}",
"store_shared": "Sdíleno do komunitního obchodu, čeká na kontrolu.",
"contrib_device_changed": "Přidáno do komunitního obchodu, ale vybraný spotřebič se změnil, zatímco byl formulář otevřený. Vyberte jej v Nastavení.",
"appliance_added": "Spotřebič přidán, čeká na schválení",
"thanks_confirming": "Děkujeme za potvrzení",
"device_approved": "Schváleno komunitou",
@@ -1989,7 +2019,9 @@
"unlabel_done": "Odznačeno cyklů: {count}",
"unlabel_failed": "Odznačení selhalo: {error}",
"odometer_saved": "Celkový počet cyklů uložen",
"odometer_save_failed": "Celkový počet nelze uložit: {error}"
"odometer_save_failed": "Celkový počet nelze uložit: {error}",
"store_pick_profile_share": "Vyberte profil pro sdílení",
"remove_failed": "Odstranění selhalo: {error}"
},
"trend": {
"down": "Klesající trend",
@@ -2024,7 +2056,7 @@
"duration_weight": "Fáze 4: jak silně shoda délky trvání ovlivňuje výsledné skóre (výchozí 0,22)",
"energy_weight": "Fáze 4: jak silně shoda energie ovlivňuje výsledné skóre (výchozí 0,22)",
"profile_match_min_duration_ratio": "Fáze 1: minimální délka cyklu jako zlomek doby trvání profilu; výchozí 0,1 (10 %)",
"profile_match_max_duration_ratio": "Fáze 1: maximální délka cyklu jako zlomek doby trvání profilu; výchozí 1,5 (150 %)",
"profile_match_max_duration_ratio": "Fáze 1: maximální délka cyklu jako zlomek doby trvání profilu; výchozí 1,8 (180 %)",
"keep_min_score": "Fáze 2: minimální skóre pro setrvání v závodu; výchozí 0,1 připouští slabé shody, pozdější fáze vyberou nejlepší",
"dtw_blend": "Fáze 3: 0 = pouze základní skóre, 1 = pouze skóre DTW, 0,5 = rovnoměrné míchání (výchozí)",
"dtw_ensemble_w": "Fáze 3 (režim kombinace): váha škálovaného L1 oproti derivátnímu DTW; výchozí 0,7 upřednostňuje úroveň",
@@ -60,6 +60,7 @@
"fair_fit_tip": "Moderat matchkonsistens - nogle cyklusser, der er tildelt denne profil, har lavere konfidensscore. Mærk flere cyklusser, eller optag profilen igen for at forbedre nøjagtigheden.",
"feedback_requested": "Feedback anmodet",
"golden_cycle": "Registreret referencecyklus",
"duration_outlier": "{n} cyklus(ser) med afvigende længde",
"improving": "↗ forbedres",
"manual": "(manuelt valgt)",
"needs_review": "Trænger til gennemgang",
@@ -256,7 +257,15 @@
"hist_scan": "Scan efter cyklusser",
"hist_import_n": "Importér {n} cyklusser",
"hist_goto_cycles": "Vis mig cyklusserne",
"save_total_cycles": "Gem total"
"save_total_cycles": "Gem total",
"clear": "Ryd",
"add_brand": "Tilføj mærke",
"add_appliance": "Tilføj apparat"
},
"note": {
"off_delay": {
"effective": "En afsluttet cyklus venter reelt {eff} s, ikke {delay} s: Minimum slukket mellemrum ({gap} s) er længere og har forrang."
}
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +387,8 @@
"community_store": "Fællesskabsbutik",
"online_account": "Fællesskabsbutik og onlinefunktioner",
"import_power_history": "Importér effekthistorik",
"service_status": "Servicestatus"
"service_status": "Servicestatus",
"online_account_tab": "Online & fællesskab"
},
"health": {
"fair": "Acceptabel matchkvalitet",
@@ -523,7 +533,8 @@
"pg_tile_matched_tip": "Cyklusser der er korrekt tilskrevet et kendt program",
"pg_time_left": "Tid tilbage",
"pg_weak_match": "Svagt match",
"phase_name": "Navn",
"phase_name": "Fasenavn",
"name": "Navn",
"phase_timeline": "Fasetidslinje",
"power": "Effekt",
"profile": "Profil",
@@ -1027,6 +1038,9 @@
"imported_readonly": "Importeret fra fællesskabsbutikken. Vises til reference og matchning. Tælles ikke med i din statistik og kan ikke redigeres.",
"merge_no_imports": "Importerede optagelser kan ikke flettes ind i en rigtig cyklus. Fravælg dem for at flette.",
"store_enable_hint": "Aktivér onlinefunktioner i Indstillinger for at gennemse og importere fællesskabets referencecyklusser.",
"advisory_duration_outlier": "{n} cyklus(ser) mærket '{name}' ligger for langt fra programmets sædvanlige længde til nogensinde at kunne matche det (værst: {ratio}x). De er sandsynligvis mærket forkert, eller én optagelse har fanget to kørsler. Mærk dem om, eller del dem op, så de ikke længere skævvrider tidsestimatet for dette program.",
"standby_above_stop_title": "Trak stadig {idle} W, da cyklussen sluttede",
"standby_above_stop_sub": "{n} af de sidste {total} cyklusser. Slukkeforsinkelsen starter først, når effekten er under Stoptærskel ({stop} W), så cyklusser slutter for sent eller skal stoppes manuelt.",
"online_intro": "Del referencecyklusser med andre WashData-brugere, og download dem. Alle onlinefunktioner er valgfrie og slået fra som standard.",
"store_share_intro": "Upload denne referencecyklus, så andre med samme apparat kan bruge den. Den gennemgås, før den vises offentligt.",
"online_intro_global": "Gennemse og del referenceoptagelser med andre WashData-brugere, og bekræft apparatposter. Én forbindelse gælder for hele din WashData-integration; apparatets mærke og model angives pr. enhed under Grundlæggende. Alle onlinefunktioner er valgfrie og slået fra som standard.",
@@ -1087,7 +1101,9 @@
"odometer_intro": "Denne total kan kun stige. Sletning af en cyklusregistrering ændrer den ikke, og den tæller videre ud over grænsen for den gemte historik, så servicepåmindelser bliver ved med at være korrekte.",
"odometer_correct_hint": "Angiv dette, hvis WashData har registreret en kørsel, der aldrig har fundet sted, eller hvis apparatet allerede havde kørt cyklusser, før WashData blev installeret.",
"no_reminders_set": "Der er endnu ikke angivet nogen servicepåmindelser.",
"toast_program_armed": "Program valgt til næste cyklus: {program}"
"toast_program_armed": "Program valgt til næste cyklus: {program}",
"toast_range_required": "Angiv et gyldigt Fra/Til-interval.",
"no_profiles_option": "(ingen profiler endnu, tilføj en)"
},
"pg_desc": {
"dishwasher_end_spike_quiet_release": "Opvaskemaskine: stille sekunder efter den forventede varighed, før ventetiden på udpumpning ved cyklusslut frigives",
@@ -1105,7 +1121,7 @@
"duration_weight": "Trin 4: hvor stærkt varighedsoverensstemmelse påvirker slutscoren (standard 0,22)",
"energy_weight": "Trin 4: hvor stærkt energioverensstemmelse påvirker slutscoren (standard 0,22)",
"profile_match_min_duration_ratio": "Trin 1: korteste cykellængde som brøkdel af profilvarighed; standard 0,1 (10%)",
"profile_match_max_duration_ratio": "Trin 1: længste cykellængde som brøkdel af profilvarighed; standard 1,5 (150%)",
"profile_match_max_duration_ratio": "Trin 1: længste cykellængde som brøkdel af profilvarighed; standard 1,8 (180%)",
"keep_min_score": "Trin 2: minimumsscore for at komme videre; standard 0,1 tillader svage match, senere trin vælger det bedste",
"dtw_blend": "Trin 3: 0 = kun kernescore, 1 = kun DTW-score, 0,5 = ligelig blanding (standard)",
"dtw_ensemble_w": "Trin 3 (ensemble-tilstand): vægt på skaleret L1 kontra derivativ DTW; standard 0,7 foretrækker niveaubevidst",
@@ -1446,8 +1462,8 @@
"label": "Forsinkelse for aflæsningspåmindelse"
},
"notify_unload_repeat": {
"doc": "Bliv ved med at gensende aflæsningspåmindelsen hvert \"Forsinkelse for aflæsningspåmindelse\" minutter, indtil du åbner døren eller trykker på \"Stop påmindelser\" i notifikationen. Kræver en Dørsensorenhed (det gør selve påmindelsen). Knappen til at afvise virker på notifikationer fra Home Assistant companion-appen (mobil).",
"label": "Gentag indtil døren åbnes"
"doc": "Bliv ved med at gensende aflæsningspåmindelsen hvert \"Forsinkelse for aflæsningspåmindelse\" minutter, indtil aflæsningen er bekræftet, eller du trykker på \"Stop påmindelser\" i notifikationen. Kræver en måde at bekræfte aflæsningen på: en Dørsensorenhed, en Aflæsningsbekræftelsesenhed eller indstillingen \"Bekræft aflæsning manuelt\" nedenfor. Knappen til at afvise virker på notifikationer fra Home Assistant companion-appen (mobil).",
"label": "Gentag indtil aflæsset"
},
"off_delay": {
"doc": "Tid til at vente efter strømfald, før cyklussen erklæres afsluttet. Hvis strømmen genoptages inden for dette vindue, fortsætter cyklussen problemfrit - dette slår bro mellem pauser mellem vasketrinene. Opvaskemaskiner har lange tørrefaser (strøm fra i 20-60 min), så slukningsforsinkelsen skal overstige det for at holde hele vask+tør som én cyklus.",
@@ -1655,6 +1671,18 @@
"energy_price_dynamic": {
"label": "Tidsvægtede omkostninger",
"doc": "Beregn hver cyklus til den pris, der gjaldt i hvert øjeblik, den kørte, i stedet for den ene pris, der gjaldt, da den sluttede. Gælder kun for en prisenhed (en statisk pris kan ikke ændre sig). Kræver ingen ekstra opsætning: prisen følges live, mens cyklussen kører, og \"Behandl historik\" under Diagnostik genberegner omkostningerne for ældre cyklusser ud fra recorderen. Fra = den klassiske adfærd, én pris frosset ved slutningen."
},
"unload_confirm_entity": {
"label": "Aflæsningsbekræftelsesenhed",
"doc": "Valgfri enhed, hvis aktivering betyder, at vasken er taget ud, til maskiner der ikke kan have en dørsensor. Alle typer virker: en Zigbee- eller NFC-knap, en input_button-hjælper, en bevægelsessensor foran maskinen, en scene. Enhver tilstandsændring tæller som en bekræftelse, bortset fra at blive slukket og at blive utilgængelig. Når denne indstilles, aktiveres aflæsningspåmindelsen også på en enhed uden Dørsensorenhed."
},
"unload_track_without_door": {
"label": "Bekræft aflæsning manuelt",
"doc": "Kør aflæsningspåmindelsen på en enhed uden Dørsensorenhed og uden Aflæsningsbekræftelsesenhed, og bekræft i stedet aflæsningen fra din egen automatisering: tryk på knapenheden \"Markér som aflæsset\", eller kald handlingen ha_washdata.mark_unloaded. Ignoreres, når en Dørsensorenhed er angivet, da den allerede leverer signalet."
},
"notify_icon_color": {
"label": "Notifikationsfarve",
"doc": "Accentfarve til denne enheds notifikationer, så en vaskemaskine, en tørretumbler og en opvaskemaskine kan skelnes fra hinanden på et øjeblik. iOS farver ikonet og Liveaktivitetens fremdriftslinje på den låste skærm og i Dynamic Island. Android får den samme farve sendt, men hvad telefonen bruger den til, er op til den: nogle farver ikonet, og mange (især Samsung One UI) ignorerer den helt. Begge forstår hex-værdier (f.eks. #03A9F4). Lad stå tomt for platformens standard."
}
},
"setting_group": {
@@ -1748,7 +1776,7 @@
"all_statuses": "Alle statusser",
"ambiguous": "Tvetydigt",
"auto_detect": "Automatisk registrering",
"clear": "Klar",
"clear": "Entydigt",
"completed": "Afsluttet",
"fine_tuning": "finjusterer nu...",
"force_stopped": "Tvunget stoppet",
@@ -1867,6 +1895,7 @@
"summary": "{total} fejldetekterede cyklus(ser) udelukket: {parts}.",
"reason": {
"force_stopped": "tvangsstoppet",
"user_stopped": "stoppet manuelt",
"interrupted": "afbrudt",
"incomplete": "ufuldstændig",
"noise": "støj",
@@ -2021,7 +2050,10 @@
"unlabel_done": "Fjernede mærkning fra {count} cyklus(ser)",
"unlabel_failed": "Fjernelse af mærkning mislykkedes: {error}",
"odometer_saved": "Samlet antal cyklusser gemt",
"odometer_save_failed": "Kunne ikke gemme totalen: {error}"
"odometer_save_failed": "Kunne ikke gemme totalen: {error}",
"contrib_device_changed": "Tilføjet til fællesskabsbutikken, men apparatet blev skiftet, mens formularen var åben. Vælg det i Indstillinger.",
"store_pick_profile_share": "Vælg en profil at dele til",
"remove_failed": "Fjernelse mislykkedes: {error}"
},
"trend": {
"down": "Trend nedad",
@@ -60,6 +60,7 @@
"fair_fit_tip": "Mäßige Übereinstimmungskonsistenz – einige diesem Profil zugewiesene Zyklen weisen niedrigere Konfidenzwerte auf. Beschriften Sie weitere Zyklen oder zeichnen Sie das Profil neu auf, um die Genauigkeit zu verbessern.",
"feedback_requested": "Feedback erbeten",
"golden_cycle": "Aufgezeichneter Referenzzyklus",
"duration_outlier": "{n} Zyklus/Zyklen mit untypischer Dauer",
"improving": "↗ Verbesserung",
"manual": "(manuell ausgewählt)",
"needs_review": "Muss überprüft werden",
@@ -256,7 +257,15 @@
"hist_scan": "Nach Zyklen suchen",
"hist_import_n": "{n} Zyklen importieren",
"hist_goto_cycles": "Zeig mir die Zyklen",
"save_total_cycles": "Gesamtzahl speichern"
"save_total_cycles": "Gesamtzahl speichern",
"clear": "Leeren",
"add_brand": "Marke hinzufügen",
"add_appliance": "Gerät hinzufügen"
},
"note": {
"off_delay": {
"effective": "Ein beendeter Zyklus wartet tatsächlich {eff} s statt {delay} s: Die Mindest-Aus-Lücke ({gap} s) ist länger und hat Vorrang."
}
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +387,8 @@
"community_store": "Community-Store",
"online_account": "Community-Store und Onlinefunktionen",
"import_power_history": "Leistungsverlauf importieren",
"service_status": "Wartungsstatus"
"service_status": "Wartungsstatus",
"online_account_tab": "Online & Community"
},
"health": {
"fair": "Akzeptable Übereinstimmungsqualität",
@@ -523,7 +533,8 @@
"pg_tile_matched_tip": "Zyklen, die korrekt einem bekannten Programm zugeordnet wurden",
"pg_time_left": "Verbleibende Zeit",
"pg_weak_match": "Schwache Übereinstimmung",
"phase_name": "Name",
"phase_name": "Phasenname",
"name": "Name",
"phase_timeline": "Phasen-Zeitachse",
"power": "Leistung",
"profile": "Profil",
@@ -1027,6 +1038,9 @@
"imported_readonly": "Aus dem Community-Store importiert. Wird zur Referenz und zum Abgleich angezeigt. Zählt nicht in Ihre Statistik und kann nicht bearbeitet werden.",
"merge_no_imports": "Importierte Aufnahmen können nicht mit einem echten Zyklus zusammengeführt werden. Heben Sie die Auswahl auf, um zusammenzuführen.",
"store_enable_hint": "Aktivieren Sie die Onlinefunktionen in den Einstellungen, um Referenzzyklen der Community zu durchsuchen und zu importieren.",
"advisory_duration_outlier": "{n} Zyklus/Zyklen mit der Bezeichnung '{name}' weichen zu stark von dessen üblicher Dauer ab, um jemals zugeordnet werden zu können (schlimmster Fall: {ratio}x). Wahrscheinlich sind sie falsch beschriftet, oder eine Aufzeichnung hat zwei Durchläufe erfasst. Beschriften Sie sie neu oder teilen Sie sie auf, damit sie die Zeitschätzung dieses Programms nicht weiter verzerren.",
"standby_above_stop_title": "Zieht beim Zyklusende noch {idle} W",
"standby_above_stop_sub": "{n} der letzten {total} Zyklen. Die Abschaltverzögerung beginnt erst, wenn die Leistung unter den Stoppschwellenwert ({stop} W) fällt, daher enden Zyklen zu spät oder müssen von Hand gestoppt werden.",
"online_intro": "Teilen Sie Referenzzyklen mit anderen WashData-Nutzern und laden Sie welche herunter. Alle Onlinefunktionen sind optional und standardmäßig deaktiviert.",
"store_share_intro": "Laden Sie diesen Referenzzyklus hoch, damit andere mit demselben Gerät ihn nutzen können. Er wird geprüft, bevor er öffentlich erscheint.",
"online_intro_global": "Durchsuchen und teilen Sie Referenzaufnahmen mit anderen WashData-Nutzern und bestätigen Sie Geräteeinträge. Eine Verbindung gilt für Ihre gesamte WashData-Integration; Gerätemarke und -modell werden pro Gerät unter Grundlegend festgelegt. Alle Onlinefunktionen sind optional und standardmäßig deaktiviert.",
@@ -1087,7 +1101,9 @@
"odometer_intro": "Diese Gesamtzahl steigt immer nur an. Das Löschen eines Zyklus-Eintrags ändert sie nicht, und sie zählt auch über die Grenze des gespeicherten Verlaufs hinaus weiter, damit Wartungserinnerungen korrekt bleiben.",
"odometer_correct_hint": "Setzen Sie diesen Wert, wenn WashData einen Lauf erfasst hat, der nie stattgefunden hat, oder wenn das Gerät schon vor der Installation von WashData Zyklen absolviert hat.",
"no_reminders_set": "Es sind noch keine Service-Erinnerungen eingerichtet.",
"toast_program_armed": "Programm für den nächsten Zyklus vorgemerkt: {program}"
"toast_program_armed": "Programm für den nächsten Zyklus vorgemerkt: {program}",
"toast_range_required": "Legen Sie einen gültigen Von/Bis-Bereich fest.",
"no_profiles_option": "(noch keine Profile, eines hinzufügen)"
},
"pg_desc": {
"dishwasher_end_spike_quiet_release": "Geschirrspüler: Ruhesekunden nach der erwarteten Dauer, bevor die Wartezeit auf das Abpumpen am Zyklusende freigegeben wird",
@@ -1105,7 +1121,7 @@
"duration_weight": "Stufe 4: wie stark die Dauerübereinstimmung den Endscore beeinflusst (Standard 0,22)",
"energy_weight": "Stufe 4: wie stark die Energieübereinstimmung den Endscore beeinflusst (Standard 0,22)",
"profile_match_min_duration_ratio": "Stufe 1: minimale Zyklusdauer als Bruchteil der Profildauer; Standard 0,1 (10%)",
"profile_match_max_duration_ratio": "Stufe 1: maximale Zyklusdauer als Bruchteil der Profildauer; Standard 1,5 (150%)",
"profile_match_max_duration_ratio": "Stufe 1: maximale Zyklusdauer als Bruchteil der Profildauer; Standard 1,8 (180%)",
"keep_min_score": "Stufe 2: Mindest-Score, um im Auswahlverfahren zu bleiben; Standard 0,1 lässt schwache Kandidaten zu, spätere Stufen wählen den besten",
"dtw_blend": "Stufe 3: 0 = nur Kernscore, 1 = nur DTW-Score, 0,5 = gleiche Mischung (Standard)",
"dtw_ensemble_w": "Stufe 3 (Ensemble-Modus): Gewicht auf skaliertem L1 gegenüber Ableitungs-DTW; Standard 0,7 bevorzugt pegelabhängige Erkennung",
@@ -1442,8 +1458,8 @@
"label": "Verzögerung der Entladeerinnerung"
},
"notify_unload_repeat": {
"doc": "Die Entladeerinnerung wird alle \"Verzögerung der Entladeerinnerung\" Minuten erneut gesendet, bis Sie die Tür öffnen oder in der Benachrichtigung auf \"Nicht mehr erinnern\" tippen. Erfordert eine Türsensor-Entität (die Erinnerung selbst benötigt sie ohnehin). Die Schaltfläche zum Verwerfen funktioniert bei Benachrichtigungen der Home Assistant Companion-App (mobil).",
"label": "Wiederholen, bis die Tür geöffnet wird"
"doc": "Die Entladeerinnerung wird alle \"Verzögerung der Entladeerinnerung\" Minuten erneut gesendet, bis die Entladung bestätigt wird oder Sie in der Benachrichtigung auf \"Nicht mehr erinnern\" tippen. Erfordert eine Möglichkeit, die Entladung zu bestätigen: eine Türsensor-Entität, eine Entladebestätigungs-Entität oder die Option \"Entladung manuell bestätigen\" weiter unten. Die Schaltfläche zum Verwerfen funktioniert bei Benachrichtigungen der Home Assistant Companion-App (mobil).",
"label": "Wiederholen, bis entladen"
},
"off_delay": {
"doc": "Zeit, die nach einem Stromausfall gewartet werden muss, bevor der Zyklus als beendet erklärt wird. Wenn die Stromversorgung innerhalb dieses Fensters wiederhergestellt wird, läuft der Zyklus nahtlos weiter – dies überbrückt Pausen zwischen den Waschphasen. Geschirrspüler haben lange Trocknungsphasen (Ausschalten für 20–60 Minuten), daher muss die Ausschaltverzögerung länger sein, damit der gesamte Spül- und Trockenvorgang als ein Zyklus erfolgt.",
@@ -1655,6 +1671,18 @@
"energy_price_dynamic": {
"label": "Zeitgewichtete Kosten",
"doc": "Berechnet jeden Zyklus zu dem Preis, der in jedem Moment seines Laufs galt, statt zu dem einen Preis, der bei Zyklusende aktuell war. Gilt nur für eine Preis-Entität (ein statischer Preis kann sich nicht ändern). Erfordert keine zusätzliche Einrichtung: Während der Zyklus läuft, wird der Preis live mitverfolgt, und „Verlauf verarbeiten“ unter Diagnose berechnet die Kosten älterer Zyklen aus dem Recorder neu. Aus = das klassische Verhalten, ein einzelner am Ende eingefrorener Preis."
},
"unload_confirm_entity": {
"label": "Entladebestätigungs-Entität",
"doc": "Optionale Entität, deren Auslösung bedeutet, dass die Ladung entnommen wurde, für Maschinen ohne Türsensor. Jede Art funktioniert: ein Zigbee- oder NFC-Taster, ein input_button-Helfer, ein Bewegungsmelder vor der Maschine, eine Szene. Jede Zustandsänderung zählt als Bestätigung, außer dem Ausschalten und dem Wechsel auf nicht verfügbar. Diese Einstellung aktiviert die Entladeerinnerung auch auf einem Gerät ohne Türsensor-Entität."
},
"unload_track_without_door": {
"label": "Entladung manuell bestätigen",
"doc": "Führt die Entladeerinnerung auf einem Gerät ohne Türsensor-Entität und ohne Entladebestätigungs-Entität aus; die Entladung bestätigen Sie stattdessen aus Ihrer eigenen Automatisierung: die Schaltflächen-Entität \"Als entladen markieren\" drücken oder die Aktion ha_washdata.mark_unloaded aufrufen. Wird ignoriert, wenn eine Türsensor-Entität festgelegt ist, die das Signal bereits liefert."
},
"notify_icon_color": {
"label": "Benachrichtigungsfarbe",
"doc": "Akzentfarbe für die Benachrichtigungen dieses Geräts, damit Waschmaschine, Trockner und Geschirrspüler auf einen Blick unterscheidbar sind. iOS färbt das Symbol und den Fortschrittsbalken der Live-Aktivität auf dem Sperrbildschirm und in der Dynamic Island. An Android wird dieselbe Farbe gesendet, aber was damit geschieht, entscheidet das Telefon: manche färben das Symbol ein, viele (insbesondere Samsung One UI) ignorieren sie ganz. Beide verstehen Hex-Werte (z. B. #03A9F4). Für den Plattformstandard lassen Sie das Feld leer."
}
},
"setting_group": {
@@ -1748,7 +1776,7 @@
"all_statuses": "Alle Status",
"ambiguous": "Mehrdeutig",
"auto_detect": "Automatische Erkennung",
"clear": "Klar",
"clear": "Eindeutig",
"completed": "Abgeschlossen",
"fine_tuning": "Jetzt Feinabstimmung…",
"force_stopped": "Zwangsweise gestoppt",
@@ -1867,6 +1895,7 @@
"summary": "{total} falsch erkannte Zyklen ausgeschlossen: {parts}.",
"reason": {
"force_stopped": "zwangsgestoppt",
"user_stopped": "manuell gestoppt",
"interrupted": "unterbrochen",
"incomplete": "unvollständig",
"noise": "Rauschen",
@@ -2021,7 +2050,10 @@
"unlabel_done": "Beschriftung von {count} Zyklen entfernt",
"unlabel_failed": "Entfernen der Beschriftung fehlgeschlagen: {error}",
"odometer_saved": "Gesamtzahl der Zyklen gespeichert",
"odometer_save_failed": "Gesamtzahl konnte nicht gespeichert werden: {error}"
"odometer_save_failed": "Gesamtzahl konnte nicht gespeichert werden: {error}",
"contrib_device_changed": "Zum Community-Store hinzugefügt, aber das Gerät wurde gewechselt, während das Formular offen war. Wählen Sie es in den Einstellungen aus.",
"store_pick_profile_share": "Wählen Sie ein Profil zum Teilen aus",
"remove_failed": "Entfernen fehlgeschlagen: {error}"
},
"trend": {
"down": "Abwärtstrend",
@@ -83,7 +83,8 @@
"not_importable": "μη διαθέσιμο εδώ",
"exists": "υπάρχει",
"backfilled_tip": "Ανιχνεύθηκε σε εισαγόμενο ιστορικό ισχύος. Διαμορφώνει μόνο την αντιστοίχιση προγραμμάτων, δεν προσμετράται στα στατιστικά.",
"armed": "(εφαρμόζεται στον επόμενο κύκλο)"
"armed": "(εφαρμόζεται στον επόμενο κύκλο)",
"duration_outlier": "{n} κύκλοι με ασυνήθιστη διάρκεια"
},
"btn": {
"reset_zoom": "Επαναφορά ζουμ",
@@ -256,7 +257,10 @@
"hist_scan": "Σάρωση για κύκλους",
"hist_import_n": "Εισαγωγή {n} κύκλων",
"hist_goto_cycles": "Εμφάνιση κύκλων",
"save_total_cycles": "Αποθήκευση συνόλου"
"save_total_cycles": "Αποθήκευση συνόλου",
"clear": "Καθαρισμός",
"add_brand": "Προσθήκη μάρκας",
"add_appliance": "Προσθήκη συσκευής"
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +382,8 @@
"community_store": "Κοινοτικό Κατάστημα",
"online_account": "Κοινοτικό Κατάστημα & διαδικτυακές λειτουργίες",
"import_power_history": "Εισαγωγή ιστορικού ισχύος",
"service_status": "Κατάσταση συντήρησης"
"service_status": "Κατάσταση συντήρησης",
"online_account_tab": "Διαδικτυακά και κοινότητα"
},
"health": {
"fair": "Αποδεκτή ποιότητα αντιστοίχισης",
@@ -518,7 +523,8 @@
"pg_tile_matched_tip": "Κύκλοι που αποδόθηκαν σωστά σε γνωστό πρόγραμμα",
"pg_time_left": "Χρόνος που απομένει",
"pg_weak_match": "Ασθενής αντιστοίχιση",
"phase_name": "Ονομα",
"phase_name": "Όνομα φάσης",
"name": "Ονομα",
"phase_timeline": "Χρονολόγιο φάσεων",
"power": "Ισχύς",
"profile": "Προφίλ",
@@ -1087,7 +1093,12 @@
"odometer_intro": "Αυτό το σύνολο μόνο αυξάνεται. Η διαγραφή της εγγραφής ενός κύκλου δεν το αλλάζει και συνεχίζει να μετρά πέρα από το όριο του αποθηκευμένου ιστορικού, ώστε οι υπενθυμίσεις σέρβις να παραμένουν σωστές.",
"odometer_correct_hint": "Ορίστε αυτή την τιμή αν το WashData κατέγραψε μια λειτουργία που δεν έγινε ποτέ, ή αν η συσκευή είχε εκτελέσει κύκλους πριν από την εγκατάσταση του WashData.",
"no_reminders_set": "Δεν έχουν οριστεί ακόμη υπενθυμίσεις σέρβις.",
"toast_program_armed": "Πρόγραμμα ορίστηκε για τον επόμενο κύκλο: {program}"
"toast_program_armed": "Πρόγραμμα ορίστηκε για τον επόμενο κύκλο: {program}",
"standby_above_stop_title": "Εξακολουθεί να καταναλώνει {idle} W όταν ο κύκλος τελείωσε",
"standby_above_stop_sub": "{n} από τους τελευταίους {total} κύκλους. Η Καθυστέρηση Απενεργοποίησης ξεκινά μόνο όταν η ισχύς πέσει κάτω από το Κατώφλι Τερματισμού ({stop} W), οπότε οι κύκλοι τελειώνουν με καθυστέρηση ή πρέπει να τερματίζονται με το χέρι.",
"advisory_duration_outlier": "{n} κύκλοι με την ετικέτα '{name}' απέχουν υπερβολικά από τη συνήθη διάρκειά του για να αντιστοιχιστούν ποτέ με αυτό (χειρότερη περίπτωση: {ratio}x). Πιθανότατα έχουν λάθος ετικέτα ή μία καταγραφή κατέγραψε δύο εκτελέσεις. Αλλάξτε τους ετικέτα ή χωρίστε τους, ώστε να σταματήσουν να αλλοιώνουν την εκτίμηση χρόνου αυτού του προγράμματος.",
"toast_range_required": "Ορίστε ένα έγκυρο εύρος Από/Έως",
"no_profiles_option": "(δεν υπάρχουν προφίλ, προσθέστε ένα)"
},
"phase_desc": {
"anti_crease": "Περιστασιακά σύντομες ανατροπές μετά την ολοκλήρωση για μείωση των ρυτίδων.",
@@ -1402,8 +1413,8 @@
"label": "Καθυστέρηση Υπενθύμισης Αποφόρτωσης"
},
"notify_unload_repeat": {
"doc": "Στέλνει ξανά την υπενθύμιση αποφόρτωσης κάθε \"Καθυστέρηση Υπενθύμισης Αποφόρτωσης\" λεπτά, μέχρι να ανοίξετε την πόρτα ή να πατήσετε \"Διακοπή υπενθυμίσεων\" στην ειδοποίηση. Απαιτεί Οντότητα Αισθητήρα Πόρτας (όπως και η ίδια η υπενθύμιση). Το κουμπί απόρριψης λειτουργεί σε ειδοποιήσεις της εφαρμογής-συντρόφου (κινητό) του Home Assistant.",
"label": "Επανάληψη μέχρι να ανοίξει η πόρτα"
"doc": "Στέλνει ξανά την υπενθύμιση αποφόρτωσης κάθε \"Καθυστέρηση Υπενθύμισης Αποφόρτωσης\" λεπτά, μέχρι να επιβεβαιωθεί η αποφόρτωση ή να πατήσετε \"Διακοπή υπενθυμίσεων\" στην ειδοποίηση. Απαιτεί έναν τρόπο επιβεβαίωσης της αποφόρτωσης: μια Οντότητα Αισθητήρα Πόρτας, μια Οντότητα Επιβεβαίωσης Αποφόρτωσης ή την επιλογή \"Χειροκίνητη Επιβεβαίωση Αποφόρτωσης\" παρακάτω. Το κουμπί απόρριψης λειτουργεί σε ειδοποιήσεις της εφαρμογής-συντρόφου (κινητό) του Home Assistant.",
"label": "Επανάληψη μέχρι την αποφόρτωση"
},
"off_delay": {
"doc": "Χρόνος αναμονής μετά την πτώση του ρεύματος πριν δηλώσετε ότι ο κύκλος έχει ολοκληρωθεί. Εάν επανέλθει η τροφοδοσία σε αυτό το παράθυρο, ο κύκλος συνεχίζεται απρόσκοπτα - αυτό γεφυρώνει τις παύσεις μεταξύ των σταδίων πλύσης. Τα πλυντήρια πιάτων έχουν μεγάλες φάσεις στεγνώματος (απενεργοποίηση για 20-60 λεπτά), επομένως η καθυστέρηση εκτός λειτουργίας πρέπει να υπερβαίνει αυτήν για να διατηρηθεί ολόκληρο το πλύσιμο+στεγνό σε έναν κύκλο.",
@@ -1623,6 +1634,18 @@
"curve_preroll_seconds": {
"label": "Προεγγραφή Καμπύλης",
"doc": "Πόσα δευτερόλεπτα μετρήσεων από ματαιωμένες απόπειρες εκκίνησης μπορεί να μεταφέρει το WashData στην αρχή της καμπύλης ενός κύκλου. Η καμπύλη ενός κύκλου ξεκινά κανονικά από την απόπειρα εκκίνησης που τελικά πετυχαίνει, οπότε σε ένα μηχάνημα που δοκιμάζει μερικές φορές πριν σταθεροποιηθεί (επιλογή προγράμματος, κλείδωμα πόρτας, πρώτο γέμισμα) τα πρώτα λεπτά της πραγματικής δραστηριότητας λείπουν από κάθε καμπύλη. Ορίστε την τιμή λίγο πάνω από τη μεγαλύτερη τέτοια περίοδο προετοιμασίας που έχει το μηχάνημά σας (τα 300 δευτ. αρκούν για τα περισσότερα). Το 0 την απενεργοποιεί. Προσοχή: η ενεργοποίηση μετατοπίζει νωρίτερα την καταγεγραμμένη έναρξη, οπότε οι κύκλοι που καταγράφηκαν πριν και μετά την αλλαγή έχουν διαφορετικές διάρκειες για το ίδιο πρόγραμμα. Οι μέσοι όροι που έχουν μαθευτεί θα αποκλίνουν μέχρι να φύγουν οι παλαιότεροι κύκλοι."
},
"unload_confirm_entity": {
"label": "Οντότητα Επιβεβαίωσης Αποφόρτωσης",
"doc": "Προαιρετική οντότητα της οποίας η ενεργοποίηση σημαίνει ότι το φορτίο αφαιρέθηκε, για μηχανήματα που δεν μπορούν να έχουν αισθητήρα πόρτας. Λειτουργεί κάθε είδους: ένα κουμπί Zigbee ή NFC, ένας βοηθός input_button, ένας αισθητήρας κίνησης μπροστά από το μηχάνημα, μια σκηνή. Κάθε αλλαγή κατάστασης μετράει ως επιβεβαίωση, εκτός από την απενεργοποίηση και τη μετάβαση σε μη διαθέσιμη. Η ρύθμιση αυτή ενεργοποιεί επίσης την υπενθύμιση αποφόρτωσης σε συσκευή χωρίς Οντότητα Αισθητήρα Πόρτας."
},
"unload_track_without_door": {
"label": "Χειροκίνητη Επιβεβαίωση Αποφόρτωσης",
"doc": "Εκτελεί την υπενθύμιση αποφόρτωσης σε συσκευή χωρίς Οντότητα Αισθητήρα Πόρτας και χωρίς Οντότητα Επιβεβαίωσης Αποφόρτωσης, και η επιβεβαίωση γίνεται από τον δικό σας αυτοματισμό: πατήστε την οντότητα κουμπιού \"Σήμανση Αποφόρτωσης\" ή καλέστε την υπηρεσία ha_washdata.mark_unloaded. Αγνοείται όταν έχει οριστεί Οντότητα Αισθητήρα Πόρτας, η οποία παρέχει ήδη το σήμα."
},
"notify_icon_color": {
"label": "Χρώμα Ειδοποίησης",
"doc": "Χρώμα τονισμού για τις ειδοποιήσεις αυτής της συσκευής, ώστε το πλυντήριο, το στεγνωτήριο και το πλυντήριο πιάτων να ξεχωρίζουν με μια ματιά. Το iOS χρωματίζει το εικονίδιο και τη γραμμή προόδου της Ζωντανής δραστηριότητας στην οθόνη κλειδώματος και στο Dynamic Island. Στο Android αποστέλλεται το ίδιο χρώμα, αλλά το τι θα το κάνει εξαρτάται από το τηλέφωνο: ορισμένα χρωματίζουν το εικονίδιο, ενώ πολλά (ιδίως το Samsung One UI) το αγνοούν εντελώς. Και τα δύο κατανοούν τον δεκαεξαδικό (Hex) κωδικό (π.χ. #03A9F4). Αφήστε κενό για την προεπιλογή της πλατφόρμας."
}
},
"setting_group": {
@@ -1716,7 +1739,7 @@
"all_statuses": "Όλες οι καταστάσεις",
"ambiguous": "Ασαφής",
"auto_detect": "Αυτόματος εντοπισμός",
"clear": "Καθαρισμός",
"clear": "Σαφής",
"completed": "Ολοκληρώθηκε",
"fine_tuning": "τελειοποίηση τώρα…",
"force_stopped": "Αναγκαστική διακοπή",
@@ -1766,6 +1789,7 @@
},
"toast": {
"catalog_refreshed": "Ο κατάλογος της κοινότητας ανανεώθηκε",
"contrib_device_changed": "Προστέθηκε στο κοινοτικό κατάστημα, αλλά η επιλεγμένη συσκευή άλλαξε όσο η φόρμα ήταν ανοιχτή. Επιλέξτε την στις Ρυθμίσεις.",
"access_saved": "Ο έλεγχος πρόσβασης αποθηκεύτηκε",
"all_wiped": "Όλα τα δεδομένα διαγράφηκαν",
"analysis_complete_none": "Η ανάλυση ολοκληρώθηκε: δεν υπάρχουν νέες προτάσεις",
@@ -1878,7 +1902,9 @@
"unlabel_done": "Αφαιρέθηκε η επισήμανση από {count} κύκλους",
"unlabel_failed": "Η αφαίρεση επισήμανσης απέτυχε: {error}",
"odometer_saved": "Το σύνολο κύκλων αποθηκεύτηκε",
"odometer_save_failed": "Δεν ήταν δυνατή η αποθήκευση του συνόλου: {error}"
"odometer_save_failed": "Δεν ήταν δυνατή η αποθήκευση του συνόλου: {error}",
"store_pick_profile_share": "Επιλέξτε ένα προφίλ για κοινοποίηση",
"remove_failed": "Η αφαίρεση απέτυχε: {error}"
},
"suggestion": {
"both_agree": "Το WashData προτείνει",
@@ -1978,6 +2004,7 @@
"summary": "Εξαιρέθηκαν {total} εσφαλμένα εντοπισμένοι κύκλοι: {parts}.",
"reason": {
"force_stopped": "αναγκαστικά τερματισμένοι",
"user_stopped": "τερματισμένοι χειροκίνητα",
"interrupted": "διακομμένοι",
"incomplete": "ημιτελείς",
"noise": "θόρυβος",
@@ -2021,7 +2048,7 @@
"duration_weight": "Στάδιο 4: πόσο επηρεάζει η συμφωνία διάρκειας τον τελικό βαθμό (προεπιλογή 0.22)",
"energy_weight": "Στάδιο 4: πόσο επηρεάζει η συμφωνία ενέργειας τον τελικό βαθμό (προεπιλογή 0.22)",
"profile_match_min_duration_ratio": "Στάδιο 1: ελάχιστο μήκος κύκλου ως κλάσμα της διάρκειας προφίλ; προεπιλογή 0.1 (10%)",
"profile_match_max_duration_ratio": "Στάδιο 1: μέγιστο μήκος κύκλου ως κλάσμα της διάρκειας προφίλ; προεπιλογή 1.5 (150%)",
"profile_match_max_duration_ratio": "Στάδιο 1: μέγιστο μήκος κύκλου ως κλάσμα της διάρκειας προφίλ; προεπιλογή 1.8 (180%)",
"keep_min_score": "Στάδιο 2: κατώτατη βαθμολογία για παραμονή στη διεκδίκηση, η προεπιλογή 0.1 επιτρέπει αδύναμες αντιστοιχίσεις και τα επόμενα στάδια επιλέγουν την καλύτερη",
"dtw_blend": "Στάδιο 3: 0 = μόνο βαθμός πυρήνα, 1 = μόνο βαθμός DTW, 0.5 = ισόρροπη ανάμειξη (προεπιλογή)",
"dtw_ensemble_w": "Στάδιο 3 (λειτουργία συνόλου): βάρος στο κλιμακωμένο-L1 έναντι παραγώγου DTW; προεπιλογή 0.7 ευνοεί το επίπεδο ισχύος",
@@ -2187,5 +2214,10 @@
"hide_guidance": "Απόκρυψη οδηγιών",
"show_guidance": "Εμφάνιση οδηγιών"
}
},
"note": {
"off_delay": {
"effective": "Ένας ολοκληρωμένος κύκλος περιμένει στην πραγματικότητα {eff} s, όχι {delay} s: το Ελάχιστο Κενό Απενεργοποίησης ({gap} s) είναι μεγαλύτερο και υπερισχύει."
}
}
}
@@ -64,6 +64,7 @@
"fair_fit_tip": "Moderate match consistency – some cycles assigned to this profile have lower confidence scores. Label more cycles or re-record the profile to improve accuracy.",
"feedback_requested": "Feedback requested",
"golden_cycle": "Recorded reference cycle",
"duration_outlier": "{n} odd-length cycle(s)",
"imported_tip": "Imported from the community store. Used for matching only, not counted in stats.",
"unmatchable": "can't be matched",
"quiet_tail": "~{mins}m quiet tail",
@@ -103,6 +104,9 @@
"back": "Back",
"browse_community_setups": "Browse community setups",
"cancel": "Cancel",
"clear": "Clear",
"add_brand": "Add brand",
"add_appliance": "Add appliance",
"clear_debug": "Clear Debug Data",
"clear_splits": "Clear",
"close": "Close",
@@ -258,6 +262,11 @@
"wipe_all": "Wipe All Data",
"save_total_cycles": "Save total"
},
"note": {
"off_delay": {
"effective": "A finished cycle actually waits {eff} s, not {delay} s: Min Off Gap ({gap} s) is longer and takes precedence."
}
},
"conflict": {
"anti_wrinkle_exit": {
"exit": "Must be below Stop Threshold ({stop} W); otherwise the anti-wrinkle exit power is ignored",
@@ -360,6 +369,7 @@
"my_preferences": "My Preferences",
"no_automations": "No automations reference this device yet.",
"online_account": "Community Store & online features",
"online_account_tab": "Online & Community",
"panel_settings": "Panel Settings",
"param_sweep": "Parameter sweep",
"pg_across_cycles": "Across your cycles",
@@ -641,7 +651,8 @@
"pg_weak_match": "Weak match",
"pg_zoom_hint": "double-click to reset zoom",
"phase": "Phase",
"phase_name": "Name",
"phase_name": "Phase Name",
"name": "Name",
"phase_timeline": "Phase timeline",
"power": "Power",
"profile": "Profile",
@@ -935,6 +946,7 @@
"no_profiles_compare": "No profiles to compare.",
"no_profiles_overlay": "No profiles to overlay.",
"no_profiles_yet": "No profiles yet. Create one from a labelled cycle.",
"no_profiles_option": "(no profiles yet - add one)",
"no_profiles_yet_short": "No profiles yet.",
"no_reminders_set": "No service reminders are set yet.",
"no_settings_match": "No settings match \"{q}\"",
@@ -1051,6 +1063,9 @@
"store_declare_appliance": "Tell WashData which appliance you own and this tab shows the setups other people have shared for it. You can also type a brand above to look around.",
"store_download_device_intro": "Adopt every shared program and its reference cycles onto your device. Your own recorded cycles and stats are not affected.",
"store_enable_hint": "Enable online features in Settings to browse and import community reference cycles.",
"advisory_duration_outlier": "{n} cycle(s) labelled ‘{name}’ are too far from its usual length to ever match it (worst: {ratio}x). They are probably mislabelled, or one recording captured two runs. Re-label or split them so they stop skewing this program’s time estimate.",
"standby_above_stop_title": "Still drawing {idle} W when the cycle ended",
"standby_above_stop_sub": "{n} of the last {total} cycles. The off delay only starts once power is below the Stop Threshold ({stop} W), so cycles finish late or need stopping by hand.",
"store_picker_offline": "Enable online features in the settings gear to pick from the community catalog.",
"store_share_device_intro": "Upload {brand} {model} with the reference cycles you select. Others with the same appliance can adopt your programs. Entries are reviewed before appearing publicly.",
"store_share_intro": "Upload this reference cycle so others with the same appliance can use it. It is reviewed before appearing publicly.",
@@ -1074,6 +1089,7 @@
"toast_failed": "Failed: {error}",
"toast_force_stop_failed": "Force stop failed: {error}",
"toast_name_required": "Name required",
"toast_range_required": "Set a valid From/To range.",
"toast_no_automation": "Cannot create automation here",
"toast_profile_name_required": "Profile name is required",
"toast_program_armed": "Program armed for the next cycle: {program}",
@@ -1110,7 +1126,7 @@
"duration_weight": "Stage 4: how strongly run-length agreement affects the final score (default 0.22)",
"energy_weight": "Stage 4: how strongly energy agreement affects the final score (default 0.22)",
"profile_match_min_duration_ratio": "Stage 1: minimum cycle length as a fraction of profile duration; default 0.1 (10%)",
"profile_match_max_duration_ratio": "Stage 1: maximum cycle length as a fraction of profile duration; default 1.5 (150%)",
"profile_match_max_duration_ratio": "Stage 1: maximum cycle length as a fraction of profile duration; default 1.8 (180%)",
"keep_min_score": "Stage 2: floor score to stay in the race; default 0.1 admits weak matches, later stages pick the best",
"dtw_blend": "Stage 3: 0 = core score only, 1 = DTW score only, 0.5 = equal blend (default)",
"dtw_ensemble_w": "Stage 3 (ensemble mode): weight on scaled-L1 vs derivative DTW; default 0.7 favours level-aware",
@@ -1460,6 +1476,10 @@
"doc": "The small icon shown on the notification (e.g. mdi:washing-machine). Android draws it in the status bar; iOS shows it in place of the app icon, and needs the companion app 2026.8 or newer. Leave blank for the platform default.",
"label": "Notification Icon"
},
"notify_icon_color": {
"doc": "Accent colour for this appliance's notifications, so a washer, a dryer and a dishwasher are tellable apart at a glance. iOS tints the icon and the Live Activity progress bar on the Lock Screen and in the Dynamic Island. Android is sent the same colour, but what it does with it is up to the phone: some tint the icon, and many (Samsung One UI especially) ignore it entirely. Hex (e.g. #03A9F4) is understood by both. Leave blank for the platform default.",
"label": "Notification Colour"
},
"notify_live_chronometer": {
"doc": "Show a live-updating countdown timer in the notification (on platforms that support it) instead of a static estimate.",
"label": "Use Live Chronometer"
@@ -1541,8 +1561,8 @@
"label": "Unload Nag Delay"
},
"notify_unload_repeat": {
"doc": "Keep re-sending the unload reminder every \"Unload Nag Delay\" minutes until you open the door or tap \"Stop reminding\" on the notification. Requires a Door Sensor Entity (the reminder itself does). The dismiss button works on Home Assistant companion-app (mobile) notifications.",
"label": "Repeat Until Door Opens"
"doc": "Keep re-sending the unload reminder every \"Unload Nag Delay\" minutes until the unload is confirmed or you tap \"Stop reminding\" on the notification. Needs a way to confirm the unload: a Door Sensor Entity, an Unload Confirmation Entity, or the \"Confirm Unload Manually\" option below. The dismiss button works on Home Assistant companion-app (mobile) notifications.",
"label": "Repeat Until Unloaded"
},
"off_delay": {
"doc": "Time to wait after power drops before declaring the cycle finished. If power resumes within this window the cycle continues seamlessly - this bridges pauses between wash stages. Dishwashers have long drying phases (power off for 20-60 min) so the off-delay must exceed that to keep the whole wash+dry as one cycle.",
@@ -1659,6 +1679,14 @@
"doc": "Optional switch toggled off on pause and back on when resuming, used together with \"Pause also cuts power\".",
"label": "Switch Entity"
},
"unload_confirm_entity": {
"doc": "Optional entity whose activation means the load has been taken out, for machines that cannot have a door sensor. Any kind works: a Zigbee or NFC button, an input_button helper, a motion sensor in front of the machine, a scene. Every state change counts as a confirmation except switching off and going unavailable. Setting this also enables the unload reminder on a device with no Door Sensor Entity.",
"label": "Unload Confirmation Entity"
},
"unload_track_without_door": {
"doc": "Run the unload reminder on a device with no Door Sensor Entity and no Unload Confirmation Entity, and confirm the unload from your own automation instead: press the \"Mark Unloaded\" button entity, or call the ha_washdata.mark_unloaded service. Ignored when a Door Sensor Entity is set, which already provides the signal.",
"label": "Confirm Unload Manually"
},
"watchdog_interval": {
"doc": "How often the background watchdog checks for stalled sensors and elapsed timeouts. Default 30 s.",
"label": "Watchdog Interval"
@@ -1874,6 +1902,7 @@
"summary": "Excluded {total} mis-detected cycle(s): {parts}.",
"reason": {
"force_stopped": "force-stopped",
"user_stopped": "manually stopped",
"interrupted": "interrupted",
"incomplete": "incomplete",
"noise": "noise",
@@ -1924,6 +1953,7 @@
"automation_deleted": "Automation deleted",
"brand_added": "Brand added - awaiting approval",
"catalog_refreshed": "Community catalog refreshed",
"contrib_device_changed": "Added to the community store, but the device changed while the form was open. Select it in Settings.",
"correction_submitted": "Correction submitted",
"cycle_deleted": "Cycle deleted",
"cycle_force_stopped": "Cycle force-stopped",
@@ -1933,6 +1963,7 @@
"cycle_trimmed": "Cycle trimmed",
"cycles_merged": "Cycles merged",
"delete_failed": "Delete failed: {error}",
"remove_failed": "Remove failed: {error}",
"delete_partial_failed": "Some items could not be deleted and were restored",
"device_approved": "Approved by the community",
"envelope_rebuilt": "Envelope rebuilt",
@@ -2018,6 +2049,7 @@
"store_name_required": "Enter a profile name",
"store_no_appliance": "Set your appliance brand and model in Settings first.",
"store_pick_profile": "Pick a profile to merge into",
"store_pick_profile_share": "Pick a profile to share into",
"store_program_required": "Enter a program name",
"store_search_failed": "Search failed: {error}",
"store_share_failed": "Share failed: {error}",
@@ -35,7 +35,8 @@
"not_importable": "n/d aquí",
"exists": "ya existe",
"backfilled_tip": "Detectado en un historial de potencia importado. Solo influye en la coincidencia de programas, no cuenta en las estadísticas.",
"armed": "(se aplica al siguiente ciclo)"
"armed": "(se aplica al siguiente ciclo)",
"duration_outlier": "{n} ciclo(s) de duración anómala"
},
"btn": {
"reset_zoom": "Restablecer zoom",
@@ -56,7 +57,7 @@
"auto_label_cycles_tip": "Asignar automáticamente nombres de perfil a ciclos sin etiquetar cuya confianza de coincidencia supera el umbral",
"cancel": "Cancelar",
"clear_debug": "Borrar datos de depuración",
"clear_splits": "Claro",
"clear_splits": "Borrar",
"close": "Cerrar",
"compare": "Comparar",
"confirm": "Confirmar",
@@ -208,7 +209,10 @@
"hist_scan": "Buscar ciclos",
"hist_import_n": "Importar {n} ciclos",
"hist_goto_cycles": "Ver los ciclos",
"save_total_cycles": "Guardar total"
"save_total_cycles": "Guardar total",
"clear": "Borrar",
"add_brand": "Añadir marca",
"add_appliance": "Añadir electrodoméstico"
},
"conflict": {
"anti_wrinkle_exit": {
@@ -330,7 +334,8 @@
"community_store": "Tienda de la comunidad",
"online_account": "Tienda de la comunidad y funciones en línea",
"import_power_history": "Importar historial de potencia",
"service_status": "Estado del mantenimiento"
"service_status": "Estado del mantenimiento",
"online_account_tab": "En línea y comunidad"
},
"health": {
"fair": "Calidad de perfil aceptable",
@@ -470,7 +475,8 @@
"pg_tile_matched_tip": "Ciclos correctamente atribuidos a un programa conocido",
"pg_time_left": "Tiempo restante",
"pg_weak_match": "Coincidencia débil",
"phase_name": "Nombre",
"phase_name": "Nombre de la fase",
"name": "Nombre",
"phase_timeline": "Cronología de fases",
"power": "Potencia",
"profile": "Perfil",
@@ -1039,7 +1045,12 @@
"odometer_intro": "Este total solo aumenta. Eliminar el registro de un ciclo no lo modifica y sigue contando más allá del límite del historial almacenado, de modo que los recordatorios de servicio siguen siendo correctos.",
"odometer_correct_hint": "Ajuste este valor si WashData registró un ciclo que nunca ocurrió, o si el electrodoméstico ya había realizado ciclos antes de instalar WashData.",
"no_reminders_set": "Aún no hay recordatorios de servicio configurados.",
"toast_program_armed": "Programa preparado para el siguiente ciclo: {program}"
"toast_program_armed": "Programa preparado para el siguiente ciclo: {program}",
"standby_above_stop_title": "Sigue consumiendo {idle} W cuando el ciclo ha terminado",
"standby_above_stop_sub": "{n} de los últimos {total} ciclos. El Retardo de apagado solo empieza cuando la potencia baja del Umbral de parada ({stop} W), así que los ciclos terminan tarde o hay que detenerlos a mano.",
"advisory_duration_outlier": "{n} ciclo(s) etiquetados como '{name}' se alejan demasiado de su duración habitual para poder coincidir alguna vez con él (el peor: {ratio}x). Probablemente estén mal etiquetados, o una grabación captó dos ejecuciones. Vuelve a etiquetarlos o divídelos para que dejen de distorsionar la estimación de tiempo de este programa.",
"toast_range_required": "Establezca un rango Desde/Hasta válido",
"no_profiles_option": "(aún no hay perfiles, añade uno)"
},
"pg_desc": {
"dishwasher_end_spike_quiet_release": "Lavavajillas: segundos de silencio tras la duración esperada antes de liberar la espera del vaciado de fin de ciclo",
@@ -1063,7 +1074,7 @@
"duration_weight": "Etapa 4: cuánto influye la concordancia de duración en la puntuación final (predeterminado 0,22)",
"energy_weight": "Etapa 4: cuánto influye la concordancia de energía en la puntuación final (predeterminado 0,22)",
"profile_match_min_duration_ratio": "Etapa 1: duración mínima del ciclo como fracción de la duración del perfil; predeterminado 0,1 (10%)",
"profile_match_max_duration_ratio": "Etapa 1: duración máxima del ciclo como fracción de la duración del perfil; predeterminado 1,5 (150%)",
"profile_match_max_duration_ratio": "Etapa 1: duración máxima del ciclo como fracción de la duración del perfil; predeterminado 1,8 (180%)",
"keep_min_score": "Etapa 2: puntuación mínima para seguir en la carrera; el valor predeterminado 0,1 admite coincidencias débiles, las etapas posteriores seleccionan la mejor",
"dtw_blend": "Etapa 3: 0 = solo puntuación central, 1 = solo puntuación DTW, 0,5 = mezcla igual (predeterminado)",
"dtw_ensemble_w": "Etapa 3 (modo ensemble): peso en L1 escalado frente a DTW derivado; el predeterminado 0,7 favorece la sensibilidad al nivel",
@@ -1398,8 +1409,8 @@
"label": "Retardo del recordatorio de descarga"
},
"notify_unload_repeat": {
"doc": "Sigue reenviando el recordatorio de descarga cada \"Retardo del recordatorio de descarga\" minutos hasta que abras la puerta o toques \"Dejar de recordar\" en la notificación. Requiere una Entidad de sensor de puerta (el propio recordatorio la necesita). El botón de descarte funciona en las notificaciones de la aplicación complementaria (móvil) de Home Assistant.",
"label": "Repetir hasta que se abra la puerta"
"doc": "Sigue reenviando el recordatorio de descarga cada \"Retardo del recordatorio de descarga\" minutos hasta que se confirme la descarga o toques \"Dejar de recordar\" en la notificación. Necesita alguna forma de confirmar la descarga: una Entidad de sensor de puerta, una Entidad de confirmación de descarga o la opción \"Confirmar la descarga manualmente\" de abajo. El botón de descarte funciona en las notificaciones de la aplicación complementaria (móvil) de Home Assistant.",
"label": "Repetir hasta confirmar la descarga"
},
"off_delay": {
"doc": "Es tiempo de esperar después de que se corte la energía antes de declarar finalizado el ciclo. Si la energía se reanuda dentro de esta ventana, el ciclo continúa sin problemas; esto salva las pausas entre las etapas de lavado. Los lavavajillas tienen fases de secado largas (apagado durante 20 a 60 minutos), por lo que el retraso de apagado debe exceder eso para mantener todo el lavado y secado como un solo ciclo.",
@@ -1607,6 +1618,18 @@
"energy_price_dynamic": {
"label": "Costo ponderado en el tiempo",
"doc": "Cobra cada ciclo al precio vigente en cada momento de su ejecución, en vez de aplicarle el único precio del instante en que terminó. Solo se aplica a una entidad de precio (un precio estático no puede variar). No necesita configuración adicional: el precio se sigue en vivo mientras el ciclo avanza, y Procesar historial, en Diagnóstico, vuelve a calcular el costo de los ciclos antiguos a partir del recorder. Desactivado = el comportamiento clásico, un solo precio congelado al final."
},
"unload_confirm_entity": {
"label": "Entidad de confirmación de descarga",
"doc": "Entidad opcional cuya activación significa que se ha sacado la carga, para máquinas que no pueden tener un sensor de puerta. Sirve cualquier tipo: un botón Zigbee o NFC, un ayudante input_button, un sensor de movimiento delante de la máquina, una escena. Cualquier cambio de estado cuenta como confirmación, excepto apagarse o pasar a no disponible. Definir esto también activa el recordatorio de descarga en un dispositivo sin Entidad de sensor de puerta."
},
"unload_track_without_door": {
"label": "Confirmar la descarga manualmente",
"doc": "Ejecuta el recordatorio de descarga en un dispositivo sin Entidad de sensor de puerta ni Entidad de confirmación de descarga, y confirmas tú la descarga desde tu propia automatización: pulsa la entidad de botón \"Marcar como descargado\" o llama al servicio ha_washdata.mark_unloaded. Se ignora cuando hay una Entidad de sensor de puerta configurada, que ya proporciona la señal."
},
"notify_icon_color": {
"label": "Color de notificación",
"doc": "Color de acento para las notificaciones de este electrodoméstico, para distinguir de un vistazo una lavadora, una secadora y un lavavajillas. iOS tiñe el icono y la barra de progreso de la Actividad en vivo en la pantalla bloqueada y en la Dynamic Island. A Android se le envía el mismo color, pero lo que haga con él depende del teléfono: algunos tiñen el icono y muchos (Samsung One UI en especial) lo ignoran por completo. Ambos entienden el formato hexadecimal (por ejemplo, #03A9F4). Déjelo en blanco para el valor predeterminado de la plataforma."
}
},
"setting_group": {
@@ -1700,7 +1723,7 @@
"all_statuses": "Todos los estados",
"ambiguous": "Ambiguo",
"auto_detect": "Detección automática",
"clear": "Claro",
"clear": "Inequívoco",
"completed": "Terminado",
"fine_tuning": "afinando ahora...",
"force_stopped": "Fuerza detenida",
@@ -1839,6 +1862,7 @@
"store_share_failed": "Error al compartir: {error}",
"store_shared": "Compartido en la tienda de la comunidad, pendiente de revisión.",
"appliance_added": "Electrodoméstico añadido, a la espera de aprobación",
"contrib_device_changed": "Añadido a la tienda de la comunidad, pero el dispositivo seleccionado cambió mientras el formulario estaba abierto. Selecciónelo en Ajustes.",
"thanks_confirming": "Gracias por confirmar",
"device_approved": "Aprobado por la comunidad",
"rating_saved": "Valoración de calidad guardada",
@@ -1862,7 +1886,9 @@
"unlabel_done": "Etiqueta quitada de {count} ciclo(s)",
"unlabel_failed": "Error al quitar la etiqueta: {error}",
"odometer_saved": "Total de ciclos guardado",
"odometer_save_failed": "No se pudo guardar el total: {error}"
"odometer_save_failed": "No se pudo guardar el total: {error}",
"store_pick_profile_share": "Elija un perfil en el que compartir",
"remove_failed": "Error al quitar: {error}"
},
"suggestion": {
"both_agree": "WashData recomienda",
@@ -1962,6 +1988,7 @@
"summary": "Excluido(s) {total} ciclo(s) mal detectado(s): {parts}.",
"reason": {
"force_stopped": "detenidos a la fuerza",
"user_stopped": "detenidos manualmente",
"interrupted": "interrumpidos",
"incomplete": "incompletos",
"noise": "ruido",
@@ -2187,5 +2214,10 @@
"hold_action": "Acción al mantener pulsado",
"double_tap_action": "Acción al tocar dos veces"
}
},
"note": {
"off_delay": {
"effective": "Un ciclo terminado espera en realidad {eff} s, no {delay} s: la Brecha mínima de apagado ({gap} s) es mayor y tiene prioridad."
}
}
}
@@ -60,6 +60,7 @@
"fair_fit_tip": "Mõõdukas sobivuse järjepidevus – mõnel sellele profiilile määratud tsüklil on madalamad usaldusskoorid. Täpsuse parandamiseks märkige rohkem tsükleid või salvestage profiil uuesti.",
"feedback_requested": "Soovitakse tagasisidet",
"golden_cycle": "Salvestatud võrdlustsükkel",
"duration_outlier": "{n} ebatavalise pikkusega tsüklit",
"improving": "↗ paraneb",
"manual": "(käsitsi valitud)",
"needs_review": "Vajab ülevaatamist",
@@ -256,7 +257,15 @@
"hist_scan": "Otsi tsükleid",
"hist_import_n": "Impordi tsüklid ({n})",
"hist_goto_cycles": "Näita tsükleid",
"save_total_cycles": "Salvesta koguarv"
"save_total_cycles": "Salvesta koguarv",
"clear": "Tühjenda",
"add_brand": "Lisa bränd",
"add_appliance": "Lisa seade"
},
"note": {
"off_delay": {
"effective": "Lõppenud tsükkel ootab tegelikult {eff} s, mitte {delay} s: Min Väljalülitumise Vahe ({gap} s) on pikem ja see kehtib."
}
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +387,8 @@
"community_store": "Kogukonna pood",
"online_account": "Kogukonna pood ja veebifunktsioonid",
"import_power_history": "Võimsuse ajaloo import",
"service_status": "Hoolduse olek"
"service_status": "Hoolduse olek",
"online_account_tab": "Veeb ja kogukond"
},
"health": {
"fair": "Rahuldav profiili kvaliteet",
@@ -518,7 +528,8 @@
"pg_tile_matched_tip": "Tsüklid, mis on õigesti seostatud tuntud programmiga",
"pg_time_left": "Aega jäänud",
"pg_weak_match": "Nõrk vaste",
"phase_name": "Nimi",
"phase_name": "Faasi nimi",
"name": "Nimi",
"phase_timeline": "Faaside ajajoon",
"power": "Võimsus",
"profile": "Profiil",
@@ -1027,6 +1038,9 @@
"imported_readonly": "Imporditud kogukonna poest. Kuvatakse viitena ja sobitamiseks. Seda ei arvestata sinu statistikas ega saa muuta.",
"merge_no_imports": "Imporditud salvestisi ei saa päris tsükliga liita. Tühista nende valik, et liitmine õnnestuks.",
"store_enable_hint": "Luba seadetes veebifunktsioonid, et sirvida ja importida kogukonna võrdlustsükleid.",
"advisory_duration_outlier": "'{name}' sildiga on {n} tsüklit, mille pikkus on selle tavapärasest kestusest liiga kaugel, et sellega üldse sobituda (halvim: {ratio}x). Tõenäoliselt on need valesti sildistatud või hõlmab üks salvestus kahte tsüklit. Sildista need ümber või jaga pooleks, et need ei moonutaks selle programmi ajahinnangut.",
"standby_above_stop_title": "Tsükli lõppedes tarbis endiselt {idle} W",
"standby_above_stop_sub": "{n} viimasest {total} tsüklist. Väljalülitumise viivitus hakkab kulgema alles siis, kui võimsus langeb alla Peatumise Läve ({stop} W), seega lõppevad tsüklid hiljaks jäädes või tuleb need käsitsi peatada.",
"online_intro": "Jaga ja laadi alla võrdlustsükleid teiste WashData kasutajatega. Kõik veebifunktsioonid on vabatahtlikud ja vaikimisi välja lülitatud.",
"store_share_intro": "Laadi see võrdlustsükkel üles, et teised sama seadmega kasutajad saaksid seda kasutada. See vaadatakse üle enne avalikuks muutumist.",
"online_intro_global": "Sirvi ja jaga võrdlussalvestisi teiste WashData kasutajatega ning kinnita seadmekirjeid. Üks ühendus kehtib kogu su WashData integratsioonile; seadme bränd ja mudel määratakse seadmepõhiselt jaotises Põhiseaded. Kõik veebifunktsioonid on vabatahtlikud ja vaikimisi välja lülitatud.",
@@ -1087,7 +1101,9 @@
"odometer_intro": "See koguarv saab ainult kasvada. Tsüklikirje kustutamine seda ei muuda ja loendamine jätkub ka üle salvestatud ajaloo piiri, nii et hoolduse meeldetuletused jäävad õigeks.",
"odometer_correct_hint": "Määra see, kui WashData salvestas käituse, mida kunagi ei toimunud, või kui seade oli tsükleid läbinud juba enne WashData paigaldamist.",
"no_reminders_set": "Hoolduse meeldetuletusi pole veel määratud.",
"toast_program_armed": "Programm seatud järgmise tsükli jaoks: {program}"
"toast_program_armed": "Programm seatud järgmise tsükli jaoks: {program}",
"toast_range_required": "Määra kehtiv Alates/Kuni vahemik.",
"no_profiles_option": "(profiile veel pole, lisa uus)"
},
"pg_desc": {
"dishwasher_end_spike_quiet_release": "Nõudepesumasin: vaiksed sekundid pärast eeldatavat kestust, enne kui tsükli lõpu tühjenduse ootamine vabastatakse",
@@ -1112,7 +1128,7 @@
"duration_weight": "Etapp 4: kui tugevalt kestuse vastavus mõjutab lõppskooris (vaikimisi 0,22)",
"energy_weight": "Etapp 4: kui tugevalt energia vastavus mõjutab lõppskooris (vaikimisi 0,22)",
"profile_match_min_duration_ratio": "Etapp 1: lühim tsükkel profiili kestuse murdosana; vaikimisi 0,1 (10%)",
"profile_match_max_duration_ratio": "Etapp 1: pikim tsükkel profiili kestuse murdosana; vaikimisi 1,5 (150%)",
"profile_match_max_duration_ratio": "Etapp 1: pikim tsükkel profiili kestuse murdosana; vaikimisi 1,8 (180%)",
"keep_min_score": "Etapp 2: madalaim skoor võistluses püsimiseks; vaikimisi 0,1 lubab nõrku kandidaate, hilisemad etapid valivad parima",
"dtw_blend": "Etapp 3: 0 = ainult põhiskoor, 1 = ainult DTW-skoor, 0,5 = võrdne segu (vaikimisi)",
"dtw_ensemble_w": "Etapp 3 (ensemble-režiim): skaleeritud L1 kaal vs tuletise DTW; vaikimisi 0,7 eelistab tasemeteadlikkust",
@@ -1446,8 +1462,8 @@
"label": "Mahalaadimise Meeldetuletuse Viivitus"
},
"notify_unload_repeat": {
"doc": "Saadab mahalaadimise meeldetuletuse iga \"Mahalaadimise Meeldetuletuse Viivitus\" minuti järel uuesti, kuni avate ukse või puudutate teavituses nuppu \"Lõpeta meeldetuletused\". Nõuab ukse anduri olemit (nagu ka meeldetuletus ise). Loobumisnupp toimib Home Assistant kaaslasrakenduse (mobiili) teavitustes.",
"label": "Korda, kuni uks avaneb"
"doc": "Saadab mahalaadimise meeldetuletuse iga „Mahalaadimise Meeldetuletuse Viivitus“ minuti järel uuesti, kuni mahalaadimine on kinnitatud või puudutate teavituses nuppu „Lõpeta meeldetuletused“. Vajab viisi mahalaadimise kinnitamiseks: Ukse Anduri Olem, Mahalaadimise Kinnituse Olem või allpool olev valik „Kinnita Mahalaadimine Käsitsi“. Loobumisnupp toimib Home Assistant kaaslasrakenduse (mobiili) teavitustes.",
"label": "Korda, kuni on maha laaditud"
},
"off_delay": {
"doc": "Aeg oodata pärast võimsuse langemist, enne kui tsükkel lõppenuks kuulutatakse. Kui toide taastub selles aknas, jätkub tsükkel sujuvalt – see sillutab pause pesuetappide vahel. Nõudepesumasinal on pikad kuivatusfaasid (väljalülitamine 20-60 min), seega peab väljalülitamise viivitus ületama seda, et hoida kogu pesu+kuiv ühe tsüklina.",
@@ -1655,6 +1671,18 @@
"energy_price_dynamic": {
"label": "Ajaliselt Kaalutud Maksumus",
"doc": "Arvestab iga tsükli maksumuse hinna järgi, mis kehtis igal selle töötamise hetkel, mitte ühe hinna järgi, mis kehtis lõpetamisel. Toimib ainult hinna olemiga (staatiline hind ei saa muutuda). Lisaseadistust pole vaja: tsükli töö ajal jälgitakse hinda reaalajas ning Diagnostika all olev toiming Töötle ajalugu arvutab vanemate tsüklite maksumuse recorderi andmetest ümber. Väljas = klassikaline käitumine, üks lõpus fikseeritud hind."
},
"unload_confirm_entity": {
"label": "Mahalaadimise Kinnituse Olem",
"doc": "Valikuline olem, mille aktiveerumine tähendab, et sisu on välja võetud; mõeldud masinatele, millele ei saa ukseandurit paigaldada. Sobib igasugune: Zigbee või NFC nupp, input_button abiline, masina ees olev liikumisandur, stseen. Kinnituseks loetakse iga olekumuutust, välja arvatud väljalülitamine ja kättesaamatuks muutumine. Selle määramine lülitab mahalaadimise meeldetuletuse sisse ka seadmel, millel Ukse Anduri Olemit ei ole."
},
"unload_track_without_door": {
"label": "Kinnita Mahalaadimine Käsitsi",
"doc": "Kasutada mahalaadimise meeldetuletust seadmel, millel pole ei Ukse Anduri Olemit ega Mahalaadimise Kinnituse Olemit, ja kinnitada mahalaadimine oma automaatikast: vajutage nupuolemit „Märgi mahalaadituks“ või kutsuge välja teenus ha_washdata.mark_unloaded. Eiratakse, kui Ukse Anduri Olem on määratud, sest see annab signaali juba ise."
},
"notify_icon_color": {
"label": "Teavituse Värv",
"doc": "Rõhuvärv selle seadme teavitustele, et pesumasin, kuivati ja nõudepesumasin oleksid ühe pilguga eristatavad. iOS värvib ikooni ja Live Activity edenemisriba lukustuskuval ning Dynamic Islandis. Androidile saadetakse sama värv, kuid mida sellega tehakse, sõltub telefonist: mõni värvib ikooni ja paljud (eriti Samsung One UI) eiravad seda täielikult. Mõlemad mõistavad heksakoodi (nt #03A9F4). Platvormi vaikeseade jaoks jätke tühjaks."
}
},
"setting_group": {
@@ -1748,7 +1776,7 @@
"all_statuses": "Kõik olekud",
"ambiguous": "Mitmetähenduslik",
"auto_detect": "Automaatne tuvastamine",
"clear": "Selge",
"clear": "Ühemõtteline",
"completed": "Lõpetatud",
"fine_tuning": "kohe peenhäälestus...",
"force_stopped": "Jõud peatus",
@@ -1798,6 +1826,7 @@
},
"toast": {
"catalog_refreshed": "Kogukonna kataloog on värskendatud",
"contrib_device_changed": "Lisatud kogukonna poodi, kuid valitud seade muutus, kui vorm oli avatud. Vali see seadetes.",
"access_saved": "Juurdepääsukontroll on salvestatud",
"all_wiped": "Kõik andmed on kustutatud",
"analysis_complete_none": "Analüüs on lõpetatud: uusi ettepanekuid pole",
@@ -1910,7 +1939,9 @@
"unlabel_done": "{count} tsükli märgistus eemaldatud",
"unlabel_failed": "Märgistuse eemaldamine ebaõnnestus: {error}",
"odometer_saved": "Tsüklite koguarv salvestatud",
"odometer_save_failed": "Koguarvu ei õnnestunud salvestada: {error}"
"odometer_save_failed": "Koguarvu ei õnnestunud salvestada: {error}",
"store_pick_profile_share": "Vali profiil, kuhu jagada",
"remove_failed": "Eemaldamine ebaõnnestus: {error}"
},
"suggestion": {
"both_agree": "WashData soovitab",
@@ -2010,6 +2041,7 @@
"summary": "Välja jäetud {total} valesti tuvastatud tsüklit: {parts}.",
"reason": {
"force_stopped": "jõuga peatatud",
"user_stopped": "käsitsi peatatud",
"interrupted": "katkestatud",
"incomplete": "lõpetamata",
"noise": "müra",
@@ -60,6 +60,7 @@
"fair_fit_tip": "Kohtalainen vastaavuus – joillakin tälle profiilille määritetyillä jaksoilla on alhaisemmat luottamuspisteet. Merkitse lisää jaksoja tai tallenna profiili uudelleen tarkkuuden parantamiseksi.",
"feedback_requested": "Palautetta pyydetty",
"golden_cycle": "Tallennettu vertailujakso",
"duration_outlier": "{n} poikkeavan pituista jaksoa",
"improving": "↗ paranee",
"manual": "(manuaalisesti valittu)",
"needs_review": "Vaatii tarkistuksen",
@@ -256,7 +257,15 @@
"hist_scan": "Etsi syklejä",
"hist_import_n": "Tuo {n} sykliä",
"hist_goto_cycles": "Näytä syklit",
"save_total_cycles": "Tallenna kokonaismäärä"
"save_total_cycles": "Tallenna kokonaismäärä",
"clear": "Tyhjennä",
"add_brand": "Lisää merkki",
"add_appliance": "Lisää laite"
},
"note": {
"off_delay": {
"effective": "Päättynyt jakso odottaa todellisuudessa {eff} s eikä {delay} s: Pienin sammutusväli ({gap} s) on pidempi ja menee edelle."
}
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +387,8 @@
"community_store": "Yhteisökauppa",
"online_account": "Yhteisökauppa ja verkkotoiminnot",
"import_power_history": "Tuo tehohistoria",
"service_status": "Huollon tila"
"service_status": "Huollon tila",
"online_account_tab": "Verkko ja yhteisö"
},
"health": {
"fair": "Tyydyttävä profiililaatu",
@@ -518,7 +528,8 @@
"pg_tile_matched_tip": "Syklit, jotka on liitetty oikein tunnettuun ohjelmaan",
"pg_time_left": "Aikaa jäljellä",
"pg_weak_match": "Heikko osuma",
"phase_name": "Nimi",
"phase_name": "Vaiheen nimi",
"name": "Nimi",
"phase_timeline": "Vaiheiden aikajana",
"power": "Teho",
"profile": "Profiili",
@@ -1027,6 +1038,9 @@
"imported_readonly": "Tuotu yhteisökaupasta. Näytetään viitteeksi ja täsmäytystä varten. Sitä ei lasketa tilastoihisi eikä sitä voi muokata.",
"merge_no_imports": "Tuotuja tallennuksia ei voi yhdistää oikeaan sykliin. Poista niiden valinta yhdistääksesi.",
"store_enable_hint": "Ota verkkotoiminnot käyttöön asetuksista selataksesi ja tuodaksesi yhteisön viitesyklejä.",
"advisory_duration_outlier": "{n} jaksoa on merkitty nimellä '{name}', mutta niiden kesto on liian kaukana sen tavallisesta pituudesta, jotta ne voisivat koskaan täsmätä siihen (pahin: {ratio}x). Ne on todennäköisesti merkitty väärin, tai yksi tallenne sisältää kaksi ajoa. Merkitse ne uudelleen tai jaa ne, jotta ne eivät vääristä tämän ohjelman aika-arviota.",
"standby_above_stop_title": "Laite otti yhä {idle} W jakson päättyessä",
"standby_above_stop_sub": "{n} viimeisestä {total} jaksosta. Sammutusviive alkaa vasta, kun teho on Pysäytyskynnyksen ({stop} W) alapuolella, joten jaksot päättyvät myöhässä tai ne on pysäytettävä käsin.",
"online_intro": "Jaa ja lataa viitesyklejä muiden WashData-käyttäjien kanssa. Kaikki verkkotoiminnot ovat vapaaehtoisia ja oletuksena pois päältä.",
"store_share_intro": "Lataa tämä viitesykli, jotta muut samaa laitetta käyttävät voivat hyödyntää sitä. Se tarkistetaan ennen julkaisua.",
"online_intro_global": "Selaa ja jaa viitetallennuksia muiden WashData-käyttäjien kanssa ja vahvista laitemerkintöjä. Yksi yhteys koskee koko WashData-integraatiotasi; laitteen merkki ja malli asetetaan laitekohtaisesti Perusmääritykset-osiossa. Kaikki verkkotoiminnot ovat vapaaehtoisia ja oletuksena pois päältä.",
@@ -1087,7 +1101,9 @@
"odometer_intro": "Tämä kokonaismäärä voi vain kasvaa. Syklitietueen poistaminen ei muuta sitä, ja laskenta jatkuu myös tallennetun historian rajan yli, jotta huoltomuistutukset pysyvät oikeina.",
"odometer_correct_hint": "Aseta tämä, jos WashData kirjasi ajon, jota ei koskaan tapahtunut, tai jos laitteella oli jo ajettu syklejä ennen WashDatan asennusta.",
"no_reminders_set": "Huoltomuistutuksia ei ole vielä asetettu.",
"toast_program_armed": "Ohjelma asetettu seuraavaa sykliä varten: {program}"
"toast_program_armed": "Ohjelma asetettu seuraavaa sykliä varten: {program}",
"toast_range_required": "Aseta kelvollinen Mistä/Mihin-alue.",
"no_profiles_option": "(ei vielä profiileja, lisää uusi)"
},
"pg_desc": {
"dishwasher_end_spike_quiet_release": "Astianpesukone: hiljaiset sekunnit odotetun keston jälkeen ennen kuin syklin lopun vedenpoiston odotus vapautetaan",
@@ -1112,7 +1128,7 @@
"duration_weight": "Vaihe 4: kuinka vahvasti keston yhdenmukaisuus vaikuttaa lopullisiin pisteisiin (oletus 0,22)",
"energy_weight": "Vaihe 4: kuinka vahvasti energian yhdenmukaisuus vaikuttaa lopullisiin pisteisiin (oletus 0,22)",
"profile_match_min_duration_ratio": "Vaihe 1: lyhin sykli profiilinkeston murto-osana; oletus 0,1 (10%)",
"profile_match_max_duration_ratio": "Vaihe 1: pisin sykli profiilinkeston murto-osana; oletus 1,5 (150%)",
"profile_match_max_duration_ratio": "Vaihe 1: pisin sykli profiilinkeston murto-osana; oletus 1,8 (180%)",
"keep_min_score": "Vaihe 2: alin pisteet pysyäkseen mukana; oletus 0,1 sallii heikot ehdokkaat, myöhemmät vaiheet valitsevat parhaan",
"dtw_blend": "Vaihe 3: 0 = vain ydinpisteet, 1 = vain DTW-pisteet, 0,5 = tasainen sekoitus (oletus)",
"dtw_ensemble_w": "Vaihe 3 (ensemble-tila): skaloidun L1 paino vs. derivatiivinen DTW; oletus 0,7 suosii tasonhavainnoivaa",
@@ -1446,8 +1462,8 @@
"label": "Purkamismuistutuksen viive"
},
"notify_unload_repeat": {
"doc": "Lähettää purkamismuistutuksen uudelleen \"Purkamismuistutuksen viive\" minuutin välein, kunnes avaat oven tai napautat ilmoituksen \"Lopeta muistuttaminen\" -painiketta. Vaatii ovianturin entiteetin (kuten itse muistutuskin). Hylkäyspainike toimii Home Assistant -kumppanisovelluksen (mobiili) ilmoituksissa.",
"label": "Toista kunnes ovi avautuu"
"doc": "Lähettää purkamismuistutuksen uudelleen ”Purkamismuistutuksen viive” minuutin välein, kunnes purkaminen on vahvistettu tai napautat ilmoituksen ”Lopeta muistuttaminen” -painiketta. Vaatii tavan vahvistaa purkaminen: ovianturin entiteetti, purkamisen vahvistusentiteetti tai alla oleva ”Vahvista purkaminen manuaalisesti” -asetus. Hylkäyspainike toimii Home Assistant -kumppanisovelluksen (mobiili) ilmoituksissa.",
"label": "Toista kunnes purettu"
},
"off_delay": {
"doc": "Aika odottaa tehon putoamisen jälkeen ennen kuin sykli julistetaan päättyneeksi. Jos virta palaa tämän ikkunan sisällä, jakso jatkuu saumattomasti - tämä siltaa taukoja pesuvaiheiden välillä. Astianpesukoneissa on pitkät kuivausvaiheet (virta pois päältä 20-60 min), joten sammutusviiveen on oltava tätä suurempi, jotta koko pesu+kuivaus pysyy yhtenä jaksona.",
@@ -1655,6 +1671,18 @@
"energy_price_dynamic": {
"label": "Aikapainotettu kustannus",
"doc": "Veloittaa kunkin syklin sillä hinnalla, joka oli voimassa jokaisella sen käyntihetkellä, yhden päättymishetken hinnan sijaan. Koskee vain hintaentiteettiä (kiinteä hinta ei voi muuttua). Ei vaadi lisäasetuksia: hintaa seurataan reaaliaikaisesti syklin käydessä, ja Diagnostiikan alla oleva toiminto Käsittele historia laskee vanhempien syklien kustannukset uudelleen recorderin tiedoista. Pois = perinteinen toiminta, yksi lopussa lukittu hinta."
},
"unload_confirm_entity": {
"label": "Purkamisen vahvistusentiteetti",
"doc": "Valinnainen entiteetti, jonka aktivoituminen tarkoittaa, että sisältö on otettu pois; tarkoitettu laitteille, joihin ei saa ovianturia. Mikä tahansa käy: Zigbee- tai NFC-painike, input_button-apuri, laitteen edessä oleva liiketunnistin, tilanne. Vahvistukseksi lasketaan jokainen tilan muutos paitsi sammuminen ja siirtyminen ei saatavissa -tilaan. Tämän asettaminen ottaa purkamismuistutuksen käyttöön myös laitteessa, jolla ei ole ovianturin entiteettiä."
},
"unload_track_without_door": {
"label": "Vahvista purkaminen manuaalisesti",
"doc": "Käytä purkamismuistutusta laitteessa, jolla ei ole ovianturin entiteettiä eikä purkamisen vahvistusentiteettiä, ja vahvista purkaminen omalla automaatiollasi: paina ”Merkitse puretuksi” -painike-entiteettiä tai kutsu palvelua ha_washdata.mark_unloaded. Ohitetaan, kun ovianturin entiteetti on asetettu, koska se antaa signaalin jo valmiiksi."
},
"notify_icon_color": {
"label": "Ilmoituksen väri",
"doc": "Korostusväri tämän laitteen ilmoituksille, jotta pesukone, kuivausrumpu ja astianpesukone erottuvat toisistaan yhdellä silmäyksellä. iOS värittää kuvakkeen ja Liveseurannan edistymispalkin lukitulla näytöllä ja Dynamic Islandissa. Androidille lähetetään sama väri, mutta puhelin päättää, mitä se sillä tekee: jotkin värittävät kuvakkeen ja monet (erityisesti Samsung One UI) jättävät sen kokonaan huomiotta. Molemmat ymmärtävät heksakoodin (esim. #03A9F4). Jätä tyhjäksi alustan oletusta varten."
}
},
"setting_group": {
@@ -1867,6 +1895,7 @@
"summary": "Suljettu pois {total} väärin tunnistettua sykliä: {parts}.",
"reason": {
"force_stopped": "pakotetusti pysäytettyä",
"user_stopped": "käsin pysäytettyä",
"interrupted": "keskeytettyä",
"incomplete": "keskeneräistä",
"noise": "kohinaa",
@@ -1909,6 +1938,7 @@
},
"toast": {
"catalog_refreshed": "Yhteisön luettelo päivitetty",
"contrib_device_changed": "Lisätty yhteisökauppaan, mutta valittu laite vaihtui lomakkeen ollessa auki. Valitse se asetuksista.",
"access_saved": "Kulunvalvonta tallennettu",
"all_wiped": "Kaikki tiedot pyyhitty",
"analysis_complete_none": "Analyysi valmis: ei uusia ehdotuksia",
@@ -2021,7 +2051,9 @@
"unlabel_done": "{count} syklin merkintä poistettu",
"unlabel_failed": "Merkinnän poisto epäonnistui: {error}",
"odometer_saved": "Syklien kokonaismäärä tallennettu",
"odometer_save_failed": "Kokonaismäärän tallennus epäonnistui: {error}"
"odometer_save_failed": "Kokonaismäärän tallennus epäonnistui: {error}",
"store_pick_profile_share": "Valitse profiili, johon jaetaan",
"remove_failed": "Poisto epäonnistui: {error}"
},
"trend": {
"down": "Trendikäs alaspäin",
@@ -35,7 +35,8 @@
"not_importable": "n/a ici",
"exists": "existe déjà",
"backfilled_tip": "Détecté dans un historique de puissance importé. Influence uniquement la correspondance des programmes, non compté dans les statistiques.",
"armed": "(s'applique au prochain cycle)"
"armed": "(s'applique au prochain cycle)",
"duration_outlier": "{n} cycle(s) de durée anormale"
},
"btn": {
"reset_zoom": "Réinitialiser le zoom",
@@ -56,7 +57,7 @@
"auto_label_cycles_tip": "Attribuer automatiquement des noms de profil aux cycles sans étiquette dont la confiance de correspondance dépasse le seuil",
"cancel": "Annuler",
"clear_debug": "Effacer les données de débogage",
"clear_splits": "Clair",
"clear_splits": "Effacer",
"close": "Fermer",
"compare": "Comparer",
"confirm": "Confirmer",
@@ -208,7 +209,10 @@
"hist_scan": "Rechercher des cycles",
"hist_import_n": "Importer {n} cycles",
"hist_goto_cycles": "Voir les cycles",
"save_total_cycles": "Enregistrer le total"
"save_total_cycles": "Enregistrer le total",
"clear": "Effacer",
"add_brand": "Ajouter une marque",
"add_appliance": "Ajouter un appareil"
},
"conflict": {
"anti_wrinkle_exit": {
@@ -330,7 +334,8 @@
"community_store": "Boutique communautaire",
"online_account": "Boutique communautaire et fonctionnalités en ligne",
"import_power_history": "Importer l'historique de puissance",
"service_status": "État de l'entretien"
"service_status": "État de l'entretien",
"online_account_tab": "En ligne et communauté"
},
"health": {
"fair": "Qualité de profil acceptable",
@@ -470,7 +475,8 @@
"pg_tile_matched_tip": "Cycles correctement attribués à un programme connu",
"pg_time_left": "Temps restant",
"pg_weak_match": "Correspondance faible",
"phase_name": "Nom",
"phase_name": "Nom de la phase",
"name": "Nom",
"phase_timeline": "Chronologie des phases",
"power": "Puissance",
"profile": "Profil",
@@ -1039,7 +1045,12 @@
"odometer_intro": "Ce total ne fait qu'augmenter. Supprimer l'enregistrement d'un cycle ne le modifie pas, et il continue de compter au-delà de la limite d'historique conservé, afin que les rappels d'entretien restent exacts.",
"odometer_correct_hint": "Renseignez cette valeur si WashData a enregistré un cycle qui n'a jamais eu lieu, ou si l'appareil avait déjà effectué des cycles avant l'installation de WashData.",
"no_reminders_set": "Aucun rappel d'entretien n'est encore configuré.",
"toast_program_armed": "Programme prévu pour le prochain cycle : {program}"
"toast_program_armed": "Programme prévu pour le prochain cycle : {program}",
"standby_above_stop_title": "Consomme encore {idle} W à la fin du cycle",
"standby_above_stop_sub": "{n} des {total} derniers cycles. Le Délai d'arrêt ne démarre qu'une fois la puissance descendue sous le Seuil d'arrêt ({stop} W) : les cycles se terminent donc en retard ou doivent être arrêtés à la main.",
"advisory_duration_outlier": "{n} cycle(s) étiquetés '{name}' s'écartent trop de sa durée habituelle pour pouvoir un jour lui correspondre (pire cas : {ratio}x). Ils sont probablement mal étiquetés, ou un enregistrement a capté deux exécutions. Réétiquetez-les ou scindez-les pour qu'ils cessent de fausser l'estimation de temps de ce programme.",
"toast_range_required": "Définissez une plage De/À valide",
"no_profiles_option": "(aucun profil, ajoutez-en un)"
},
"pg_desc": {
"dishwasher_end_spike_quiet_release": "Lave-vaisselle : secondes de silence après la durée prévue avant de libérer l'attente de vidange de fin de cycle",
@@ -1063,7 +1074,7 @@
"duration_weight": "Étape 4 : force avec laquelle la concordance de durée affecte le score final (défaut 0,22)",
"energy_weight": "Étape 4 : force avec laquelle la concordance d'énergie affecte le score final (défaut 0,22)",
"profile_match_min_duration_ratio": "Étape 1 : durée de cycle minimale en fraction de la durée du profil ; défaut 0,1 (10 %)",
"profile_match_max_duration_ratio": "Étape 1 : durée de cycle maximale en fraction de la durée du profil ; défaut 1,5 (150 %)",
"profile_match_max_duration_ratio": "Étape 1 : durée de cycle maximale en fraction de la durée du profil ; défaut 1,8 (180 %)",
"keep_min_score": "Étape 2 : score plancher pour rester en lice ; le défaut 0,1 admet les candidats faibles, les étapes suivantes sélectionnent le meilleur",
"dtw_blend": "Étape 3 : 0 = score central uniquement, 1 = score DTW uniquement, 0,5 = mélange égal (défaut)",
"dtw_ensemble_w": "Étape 3 (mode ensemble) : poids sur L1 normalisé vs DTW dérivé ; le défaut 0,7 favorise la sensibilité au niveau",
@@ -1398,8 +1409,8 @@
"label": "Délai de rappel de déchargement"
},
"notify_unload_repeat": {
"doc": "Continue de renvoyer le rappel de déchargement toutes les \"Délai de rappel de déchargement\" minutes jusqu'à ce que vous ouvriez la porte ou appuyiez sur \"Arrêter les rappels\" dans la notification. Nécessite une Entité capteur de porte (le rappel lui-même en a besoin). Le bouton de rejet fonctionne sur les notifications de l'application compagnon (mobile) de Home Assistant.",
"label": "Répéter jusqu'à l'ouverture de la porte"
"doc": "Continue de renvoyer le rappel de déchargement toutes les \"Délai de rappel de déchargement\" minutes jusqu'à ce que le déchargement soit confirmé ou que vous appuyiez sur \"Arrêter les rappels\" dans la notification. Nécessite un moyen de confirmer le déchargement : une Entité capteur de porte, une Entité de confirmation de déchargement ou l'option \"Confirmer le déchargement manuellement\" ci-dessous. Le bouton de rejet fonctionne sur les notifications de l'application compagnon (mobile) de Home Assistant.",
"label": "Répéter jusqu'au déchargement"
},
"off_delay": {
"doc": "Temps d'attente après la coupure de courant avant de déclarer le cycle terminé. Si le courant revient dans cette fenêtre, le cycle continue de manière transparente – cela comble les pauses entre les étapes de lavage. Les lave-vaisselle ont de longues phases de séchage (mise hors tension pendant 20 à 60 minutes), le délai d'arrêt doit donc être supérieur à ce délai pour que l'ensemble du lavage et du séchage se déroule en un seul cycle.",
@@ -1607,6 +1618,18 @@
"energy_price_dynamic": {
"label": "Coût pondéré dans le temps",
"doc": "Facture chaque cycle au prix en vigueur à chaque instant où il a tourné, au lieu du seul prix du moment où il s'est terminé. Ne s'applique qu'à une entité de prix (un prix statique ne peut pas varier). Aucune configuration supplémentaire n'est nécessaire : le prix est suivi en direct pendant le cycle, et Traiter l'historique, dans Diagnostic, recalcule le coût des anciens cycles à partir du recorder. Désactivé = le comportement classique, un seul prix figé à la fin."
},
"unload_confirm_entity": {
"label": "Entité de confirmation de déchargement",
"doc": "Entité facultative dont l'activation signifie que la charge a été sortie, pour les machines qui ne peuvent pas avoir de capteur de porte. N'importe quel type convient : un bouton Zigbee ou NFC, une aide input_button, un détecteur de mouvement devant la machine, une scène. Tout changement d'état compte comme une confirmation, sauf l'extinction et le passage à indisponible. Définir cette entité active aussi le rappel de déchargement sur un appareil sans Entité capteur de porte."
},
"unload_track_without_door": {
"label": "Confirmer le déchargement manuellement",
"doc": "Exécute le rappel de déchargement sur un appareil sans Entité capteur de porte ni Entité de confirmation de déchargement, et vous confirmez le déchargement depuis votre propre automatisation : appuyez sur l'entité bouton \"Marquer comme déchargé\" ou appelez le service ha_washdata.mark_unloaded. Ignoré lorsqu'une Entité capteur de porte est définie, car elle fournit déjà le signal."
},
"notify_icon_color": {
"label": "Couleur de notification",
"doc": "Couleur d'accentuation des notifications de cet appareil, afin de distinguer d'un coup d'œil un lave-linge, un sèche-linge et un lave-vaisselle. iOS colore l'icône ainsi que la barre de progression de l'activité en direct sur l'écran verrouillé et dans la Dynamic Island. Android reçoit la même couleur, mais l'usage qu'il en fait dépend du téléphone : certains colorent l'icône, et beaucoup (Samsung One UI en particulier) l'ignorent totalement. Le format hexadécimal (par exemple #03A9F4) est compris par les deux. Laissez vide pour la valeur par défaut de la plateforme."
}
},
"setting_group": {
@@ -1700,7 +1723,7 @@
"all_statuses": "Tous les statuts",
"ambiguous": "Ambiguë",
"auto_detect": "Détection automatique",
"clear": "Clair",
"clear": "Sans ambiguïté",
"completed": "Complété",
"fine_tuning": "peaufiner maintenant…",
"force_stopped": "Forcer arrêté",
@@ -1839,6 +1862,7 @@
"store_share_failed": "Échec du partage : {error}",
"store_shared": "Partagé sur la boutique communautaire, en attente de révision.",
"appliance_added": "Appareil ajouté, en attente d'approbation",
"contrib_device_changed": "Ajouté à la boutique communautaire, mais l'appareil sélectionné a changé pendant que le formulaire était ouvert. Sélectionnez-le dans les Paramètres.",
"thanks_confirming": "Merci d'avoir confirmé",
"device_approved": "Approuvé par la communauté",
"rating_saved": "Évaluation de la qualité enregistrée",
@@ -1862,7 +1886,9 @@
"unlabel_done": "Étiquette retirée de {count} cycle(s)",
"unlabel_failed": "Échec du retrait de l'étiquette : {error}",
"odometer_saved": "Total des cycles enregistré",
"odometer_save_failed": "Impossible d'enregistrer le total : {error}"
"odometer_save_failed": "Impossible d'enregistrer le total : {error}",
"store_pick_profile_share": "Choisissez un profil dans lequel partager",
"remove_failed": "Échec du retrait : {error}"
},
"suggestion": {
"both_agree": "WashData recommande",
@@ -1962,6 +1988,7 @@
"summary": "{total} cycle(s) mal détecté(s) exclu(s) : {parts}.",
"reason": {
"force_stopped": "arrêtés de force",
"user_stopped": "arrêtés manuellement",
"interrupted": "interrompus",
"incomplete": "incomplets",
"noise": "bruit",
@@ -2187,5 +2214,10 @@
"hold_action": "Action au maintien",
"double_tap_action": "Action au double toucher"
}
},
"note": {
"off_delay": {
"effective": "Un cycle terminé attend en réalité {eff} s, et non {delay} s : l'Intervalle d'arrêt minimal ({gap} s) est plus long et prime."
}
}
}
@@ -60,6 +60,7 @@
"fair_fit_tip": "Umjerena dosljednost podudaranja – neki ciklusi dodijeljeni ovom profilu imaju niže rezultate pouzdanosti. Označite više ciklusa ili ponovno snimite profil kako biste poboljšali točnost.",
"feedback_requested": "Zatražene povratne informacije",
"golden_cycle": "Snimljeni referentni ciklus",
"duration_outlier": "Ciklusi neobične duljine: {n}",
"improving": "↗ poboljšanje",
"manual": "(ručno odabrano)",
"needs_review": "Potreban pregled",
@@ -256,7 +257,15 @@
"hist_scan": "Traži cikluse",
"hist_import_n": "Uvezi cikluse: {n}",
"hist_goto_cycles": "Prikaži cikluse",
"save_total_cycles": "Spremi ukupan broj"
"save_total_cycles": "Spremi ukupan broj",
"clear": "Očisti",
"add_brand": "Dodaj marku",
"add_appliance": "Dodaj uređaj"
},
"note": {
"off_delay": {
"effective": "Završen ciklus zapravo čeka {eff} s, a ne {delay} s: Min. razmak isključivanja ({gap} s) dulji je i ima prednost."
}
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +387,8 @@
"community_store": "Zajednička trgovina",
"online_account": "Zajednička trgovina i online značajke",
"import_power_history": "Uvoz povijesti snage",
"service_status": "Status servisa"
"service_status": "Status servisa",
"online_account_tab": "Online i zajednica"
},
"health": {
"fair": "Prihvatljiva kvaliteta profila",
@@ -519,7 +529,8 @@
"pg_tile_matched_tip": "Ciklusi ispravno pripisani poznatom programu",
"pg_time_left": "Preostalo vrijeme",
"pg_weak_match": "Slabo podudaranje",
"phase_name": "Ime",
"phase_name": "Ime faze",
"name": "Ime",
"phase_timeline": "Vremenska crta faza",
"power": "Snaga",
"profile": "Profil",
@@ -1032,6 +1043,9 @@
"imported_readonly": "Uvezeno iz zajedničke trgovine. Prikazano za referencu i podudaranje. Ne broji se u vašu statistiku i ne može se uređivati.",
"merge_no_imports": "Uvezene snimke ne mogu se spojiti sa stvarnim ciklusom. Poništite njihov odabir za spajanje.",
"store_enable_hint": "Omogućite online značajke u Postavkama kako biste mogli pregledavati i uvoziti referentne cikluse zajednice.",
"advisory_duration_outlier": "Ciklusi označeni kao '{name}' ({n}) previše odstupaju od njegove uobičajene duljine da bi se ikada mogli podudariti s njim (najveće odstupanje: {ratio}x). Vjerojatno su pogrešno označeni ili je jedno snimanje obuhvatilo dva pokretanja. Ponovno ih označite ili ih podijelite kako bi prestali iskrivljavati procjenu vremena za ovaj program.",
"standby_above_stop_title": "Još uvijek povlači {idle} W kada je ciklus završio",
"standby_above_stop_sub": "{n} od posljednjih {total} ciklusa. Odgoda isključivanja počinje tek kada snaga padne ispod Praga zaustavljanja ({stop} W), pa ciklusi završavaju sa zakašnjenjem ili ih treba zaustaviti ručno.",
"online_intro": "Dijelite i preuzimajte referentne cikluse s drugim korisnicima WashData. Sve online značajke su dobrovoljne i prema zadanome isključene.",
"store_share_intro": "Prenesite ovaj referentni ciklus kako bi ga mogli koristiti drugi s istim uređajem. Pregledava se prije nego što postane javno dostupan.",
"online_intro_global": "Pregledavajte i dijelite referentne snimke s drugim korisnicima WashData te potvrđujte unose uređaja. Jedna veza vrijedi za cijelu vašu WashData integraciju; marka i model uređaja postavljaju se za svaki uređaj u odjeljku Osnovno. Sve online značajke su dobrovoljne i prema zadanome isključene.",
@@ -1087,7 +1101,9 @@
"odometer_intro": "Ovaj ukupan broj samo raste. Brisanje zapisa o ciklusu ga ne mijenja i nastavlja rasti i nakon ograničenja spremljene povijesti, pa podsjetnici za servis ostaju točni.",
"odometer_correct_hint": "Postavite ovo ako je WashData zabilježio ciklus koji se nikada nije dogodio ili ako je uređaj već obavio cikluse prije instaliranja WashData.",
"no_reminders_set": "Još nema postavljenih podsjetnika za servis.",
"toast_program_armed": "Program postavljen za sljedeći ciklus: {program}"
"toast_program_armed": "Program postavljen za sljedeći ciklus: {program}",
"toast_range_required": "Postavite ispravan raspon Od/Do",
"no_profiles_option": "(nema profila, dodajte novi)"
},
"phase_desc": {
"anti_crease": "Povremena kratka prevrtanja nakon završetka kako bi se smanjile bore.",
@@ -1414,8 +1430,8 @@
"label": "Odgoda podsjetnika za istovar"
},
"notify_unload_repeat": {
"doc": "Ponovno šalje podsjetnik za istovar svakih \"Odgoda podsjetnika za istovar\" minuta dok ne otvorite vrata ili ne dodirnete \"Prestani podsjećati\" na obavijesti. Zahtijeva Entitet senzora vrata (kao i sam podsjetnik). Gumb za odbacivanje radi na obavijestima mobilne aplikacije Home Assistant (companion app).",
"label": "Ponavljaj dok se vrata ne otvore"
"doc": "Ponovno šalje podsjetnik za istovar svakih \"Odgoda podsjetnika za istovar\" minuta dok se istovar ne potvrdi ili dok ne dodirnete \"Prestani podsjećati\" na obavijesti. Potreban je način za potvrdu istovara: Entitet senzora vrata, Entitet potvrde istovara ili opcija \"Ručno potvrdi istovar\" u nastavku. Gumb za odbacivanje radi na obavijestima mobilne aplikacije Home Assistant (companion app).",
"label": "Ponavljaj do istovara"
},
"off_delay": {
"doc": "Vrijeme čekanja nakon pada struje prije nego što se proglasi da je ciklus završen. Ako se struja nastavi unutar ovog prozora, ciklus se nastavlja neprimjetno - ovo premošćuje pauze između faza pranja. Perilice posuđa imaju duge faze sušenja (isključivanje na 20-60 min) tako da odgoda isključivanja mora biti veća od te kako bi cijeli ciklus pranja+sušenja bio jedan ciklus.",
@@ -1623,6 +1639,18 @@
"energy_price_dynamic": {
"label": "Vremenski ponderiran trošak",
"doc": "Naplaćuje svaki ciklus po cijeni koja je vrijedila u svakom trenutku njegova rada, umjesto po jednoj cijeni koja je vrijedila kada je završio. Odnosi se samo na entitet cijene (statična cijena se ne može mijenjati). Ne treba dodatno postavljanje: cijena se prati uživo dok ciklus traje, a Obrada povijesti u Dijagnostici ponovno obračunava starije cikluse iz recordera. Isključeno = klasično ponašanje, jedna cijena zamrznuta na kraju."
},
"unload_confirm_entity": {
"label": "Entitet potvrde istovara",
"doc": "Opcijski entitet čije aktiviranje znači da je sadržaj izvađen, za strojeve koji ne mogu imati senzor vrata. Radi bilo koja vrsta: Zigbee ili NFC gumb, input_button pomoćnik, senzor pokreta ispred stroja, scena. Svaka promjena stanja broji se kao potvrda, osim isključivanja i prelaska u nedostupno stanje. Postavljanjem ovoga ujedno se omogućuje podsjetnik za istovar na uređaju bez Entiteta senzora vrata."
},
"unload_track_without_door": {
"label": "Ručno potvrdi istovar",
"doc": "Pokrenite podsjetnik za istovar na uređaju bez Entiteta senzora vrata i bez Entiteta potvrde istovara te istovar potvrdite iz vlastite automatizacije: pritisnite entitet gumba \"Označi kao istovareno\" ili pozovite uslugu ha_washdata.mark_unloaded. Zanemaruje se kada je postavljen Entitet senzora vrata, koji već daje taj signal."
},
"notify_icon_color": {
"label": "Boja obavijesti",
"doc": "Naglasna boja obavijesti ovog uređaja, kako bi se perilica rublja, sušilica i perilica posuđa razlikovale na prvi pogled. iOS oboji ikonu i traku napretka Live Activity na zaključanom zaslonu i u Dynamic Island. Android dobiva istu boju, ali što će s njom učiniti ovisi o telefonu: neki oboje ikonu, a mnogi (osobito Samsung One UI) je potpuno zanemaruju. Obje platforme razumiju Hex (npr. #03A9F4). Ostavite prazno za zadanu boju platforme."
}
},
"setting_group": {
@@ -1716,7 +1744,7 @@
"all_statuses": "Svi statusi",
"ambiguous": "Dvosmislen",
"auto_detect": "Automatsko otkrivanje",
"clear": "Jasan",
"clear": "Jednoznačan",
"completed": "Završeno",
"fine_tuning": "fino podešavanje sada...",
"force_stopped": "Prisilno zaustavljeno",
@@ -1835,6 +1863,7 @@
"summary": "Isključeno {total} pogrešno otkrivenih ciklusa: {parts}.",
"reason": {
"force_stopped": "prisilno zaustavljeni",
"user_stopped": "ručno zaustavljeni",
"interrupted": "prekinuti",
"incomplete": "nepotpuni",
"noise": "buka",
@@ -1965,6 +1994,7 @@
"store_no_appliance": "Najprije postavite marku i model uređaja u Postavkama.",
"store_share_failed": "Dijeljenje nije uspjelo: {error}",
"store_shared": "Podijeljeno u zajedničku trgovinu, čeka pregled.",
"contrib_device_changed": "Dodano u zajedničku trgovinu, ali se odabrani uređaj promijenio dok je obrazac bio otvoren. Odaberite ga u Postavkama.",
"appliance_added": "Uređaj dodan, čeka odobrenje",
"thanks_confirming": "Hvala na potvrdi",
"device_approved": "Odobreno od strane zajednice",
@@ -1989,7 +2019,9 @@
"unlabel_done": "Uklonjena oznaka s {count} ciklus(a)",
"unlabel_failed": "Uklanjanje oznake nije uspjelo: {error}",
"odometer_saved": "Ukupan broj ciklusa spremljen",
"odometer_save_failed": "Nije moguće spremiti ukupan broj: {error}"
"odometer_save_failed": "Nije moguće spremiti ukupan broj: {error}",
"store_pick_profile_share": "Odaberite profil za dijeljenje",
"remove_failed": "Uklanjanje nije uspjelo: {error}"
},
"trend": {
"down": "Trend pada",
@@ -2024,7 +2056,7 @@
"duration_weight": "Faza 4: koliko jako slaganje trajanja utječe na konačnu ocjenu (zadano 0,22)",
"energy_weight": "Faza 4: koliko jako slaganje energije utječe na konačnu ocjenu (zadano 0,22)",
"profile_match_min_duration_ratio": "Faza 1: minimalna duljina ciklusa kao udio trajanja profila; zadano 0,1 (10%)",
"profile_match_max_duration_ratio": "Faza 1: maksimalna duljina ciklusa kao udio trajanja profila; zadano 1,5 (150%)",
"profile_match_max_duration_ratio": "Faza 1: maksimalna duljina ciklusa kao udio trajanja profila; zadano 1,8 (180%)",
"keep_min_score": "Faza 2: minimalna ocjena za ostanak u utrci; zadano 0,1 dopušta slabe podudarnosti, kasniji koraci biraju najboljeg",
"dtw_blend": "Faza 3: 0 = samo osnovna ocjena, 1 = samo DTW ocjena, 0,5 = jednako miješanje (zadano)",
"dtw_ensemble_w": "Faza 3 (kombinirani način): težina skaliranog L1 nasuprot derivatnog DTW; zadano 0,7 favorizira razinu",
@@ -60,6 +60,7 @@
"fair_fit_tip": "Mérsékelt egyezési konzisztencia – néhány ehhez a profilhoz rendelt ciklus alacsonyabb megbízhatósági pontszámmal rendelkezik. Címkézz fel több ciklust, vagy rögzítsd újra a profilt a pontosság javítása érdekében.",
"feedback_requested": "Visszajelzést kértünk",
"golden_cycle": "Rögzített referenciaciklus",
"duration_outlier": "{n} szokatlan hosszúságú ciklus",
"improving": "↗ javul",
"manual": "(manuálisan kiválasztott)",
"needs_review": "Felülvizsgálatra szorul",
@@ -256,7 +257,15 @@
"hist_scan": "Ciklusok keresése",
"hist_import_n": "{n} ciklus importálása",
"hist_goto_cycles": "Ciklusok megjelenítése",
"save_total_cycles": "Összesítés mentése"
"save_total_cycles": "Összesítés mentése",
"clear": "Törlés",
"add_brand": "Márka hozzáadása",
"add_appliance": "Készülék hozzáadása"
},
"note": {
"off_delay": {
"effective": "A befejezett ciklus valójában {eff} s-ot vár, nem {delay} s-ot: a Min. kikapcsolási rés ({gap} s) hosszabb, és az élvez elsőbbséget."
}
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +387,8 @@
"community_store": "Közösségi bolt",
"online_account": "Közösségi bolt és online funkciók",
"import_power_history": "Teljesítményelőzmények importálása",
"service_status": "Szerviz állapota"
"service_status": "Szerviz állapota",
"online_account_tab": "Online és közösség"
},
"health": {
"fair": "Elfogadható profilminőség",
@@ -518,7 +528,8 @@
"pg_tile_matched_tip": "Ismert programhoz helyesen hozzárendelt ciklusok",
"pg_time_left": "Hátralévő idő",
"pg_weak_match": "Gyenge egyezés",
"phase_name": "Név",
"phase_name": "Fázis neve",
"name": "Név",
"phase_timeline": "Fázisidővonal",
"power": "Teljesítmény",
"profile": "Profil",
@@ -1027,6 +1038,9 @@
"imported_readonly": "A közösségi boltból importálva. Referenciaként és egyeztetéshez jelenik meg. Nem számít bele a statisztikákba, és nem szerkeszthető.",
"merge_no_imports": "Az importált felvételek nem egyesíthetők valódi ciklussal. Az egyesítéshez szüntesse meg a kijelölésüket.",
"store_enable_hint": "A közösségi referenciaciklusok böngészéséhez és importálásához engedélyezze az online funkciókat a Beállításokban.",
"advisory_duration_outlier": "A(z) '{name}' címkével ellátott {n} ciklus hossza túlságosan eltér a szokásos hosszától ahhoz, hogy valaha is illeszkedjen hozzá (a legrosszabb: {ratio}x). Valószínűleg rosszul vannak címkézve, vagy egy felvétel két futást rögzített. Címkézze át vagy vágja szét őket, hogy ne torzítsák ennek a programnak az időbecslését.",
"standby_above_stop_title": "A ciklus végén még mindig {idle} W-ot vett fel",
"standby_above_stop_sub": "Az utolsó {total} ciklusból {n}. A kikapcsolási késleltetés csak akkor indul el, amikor a teljesítmény a Leállítási küszöb ({stop} W) alá esik, így a ciklusok későn fejeződnek be, vagy kézzel kell leállítani őket.",
"online_intro": "Ossza meg és töltsön le referenciaciklusokat más WashData-felhasználókkal. Minden online funkció opcionális és alapértelmezés szerint ki van kapcsolva.",
"store_share_intro": "Töltse fel ezt a referenciaciklust, hogy az azonos készülékkel rendelkezők is használhassák. Nyilvános megjelenés előtt ellenőrzésre kerül.",
"online_intro_global": "Böngésszen és osszon meg referenciafelvételeket más WashData-felhasználókkal, és erősítsen meg készülékbejegyzéseket. Egyetlen kapcsolat a teljes WashData-integrációra vonatkozik; a készülék márkája és modellje eszközönként, az Alapkonfiguráció szakaszban állítható be. Minden online funkció opcionális és alapértelmezés szerint ki van kapcsolva.",
@@ -1087,7 +1101,9 @@
"odometer_intro": "Ez az összesítés csak növekedhet. Egy ciklusbejegyzés törlése nem változtatja meg, és a tárolt előzmények korlátján túl is számol tovább, így a szerviz emlékeztetők pontosak maradnak.",
"odometer_correct_hint": "Akkor állítsa be, ha a WashData olyan futást rögzített, amely soha nem történt meg, vagy ha a készülék már a WashData telepítése előtt is futtatott ciklusokat.",
"no_reminders_set": "Még nincs beállítva szerviz emlékeztető.",
"toast_program_armed": "Program beállítva a következő ciklushoz: {program}"
"toast_program_armed": "Program beállítva a következő ciklushoz: {program}",
"toast_range_required": "Adjon meg érvényes Ettől/Eddig tartományt.",
"no_profiles_option": "(még nincs profil, adjon hozzá egyet)"
},
"phase_desc": {
"anti_crease": "Alkalmankénti rövid bukdácsolás a befejezés után a ráncok csökkentése érdekében.",
@@ -1406,8 +1422,8 @@
"label": "Kirakodási emlékeztető késleltetése"
},
"notify_unload_repeat": {
"doc": "Újraküldi a kirakodási emlékeztetőt \"Kirakodási emlékeztető késleltetése\" percenként, amíg ki nem nyitja az ajtót, vagy meg nem érinti az \"Emlékeztetés leállítása\" gombot az értesítésen. Ajtóérzékelő entitást igényel (ahogyan maga az emlékeztető is). Az elvetés gomb a Home Assistant kísérőalkalmazás (mobil) értesítésein működik.",
"label": "Ismétlés az ajtó kinyitásáig"
"doc": "Újraküldi a kirakodási emlékeztetőt „Kirakodási emlékeztető késleltetése” percenként, amíg a kirakodás megerősítést nem kap, vagy meg nem érinti az „Emlékeztetés leállítása” gombot az értesítésen. Szükség van a kirakodás megerősítésének valamilyen módjára: Ajtóérzékelő entitás, Kirakodást megerősítő entitás vagy az alábbi „Kirakodás kézi megerősítése” beállítás. Az elvetés gomb a Home Assistant kísérőalkalmazás (mobil) értesítésein működik.",
"label": "Ismétlés kirakodásig"
},
"off_delay": {
"doc": "Várakozási idő a teljesítmény csökkenése után, mielőtt befejezettnek nyilvánítaná a ciklust. Ha az áramellátás ezen az ablakon belül folytatódik, a ciklus zökkenőmentesen folytatódik – ez áthidalja a szüneteket a mosási fázisok között. A mosogatógépek hosszú szárítási fázisokkal rendelkeznek (20-60 percre kikapcsolva), így a kikapcsolási késleltetésnek meg kell haladnia ezt, hogy az egész mosás+szárítás egy ciklusban maradjon.",
@@ -1623,6 +1639,18 @@
"curve_preroll_seconds": {
"label": "Görbe előfutama",
"doc": "Hány másodpercnyi leolvasás kerülhet a megszakadt indítási kísérletekből a ciklus görbéjének elejére. A ciklus görbéje általában azzal az indítási kísérlettel kezdődik, amelyik végül sikerül, ezért egy olyan gépnél, amely néhányszor próbálkozik, mielőtt beállna (programválasztás, ajtózár, első vízbeeresztés), minden görbéből hiányzik a tényleges működés első néhány perce. Állítsa ezt az értéket kicsivel a gépénél előforduló leghosszabb ilyen nekifutás fölé (300 mp a legtöbb géphez bőven elég). A 0 kikapcsolja. Vegye figyelembe, hogy bekapcsolva a rögzített kezdés korábbra kerül, így a változtatás előtt és után rögzített ciklusok ugyanahhoz a programhoz eltérő időtartamot hordoznak; a megtanult átlagok addig csúsznak el, amíg a régebbi ciklusok ki nem esnek."
},
"unload_confirm_entity": {
"label": "Kirakodást megerősítő entitás",
"doc": "Opcionális entitás, amelynek aktiválása azt jelenti, hogy az adagot kivették, olyan gépekhez, amelyekre nem szerelhető ajtóérzékelő. Bármilyen típus megfelel: Zigbee vagy NFC gomb, input_button segéd, a gép előtti mozgásérzékelő, jelenet. Minden állapotváltozás megerősítésnek számít, kivéve a kikapcsolást és az elérhetetlenné válást. Ennek beállítása bekapcsolja a kirakodási emlékeztetőt olyan eszközön is, amelyhez nincs Ajtóérzékelő entitás."
},
"unload_track_without_door": {
"label": "Kirakodás kézi megerősítése",
"doc": "Futtatja a kirakodási emlékeztetőt olyan eszközön, amelyhez nincs Ajtóérzékelő entitás és nincs Kirakodást megerősítő entitás, a kirakodást pedig saját automatizálásból erősíti meg: nyomja meg a „Megjelölés kirakodottként” gombentitást, vagy hívja meg a ha_washdata.mark_unloaded szolgáltatást. Figyelmen kívül marad, ha be van állítva Ajtóérzékelő entitás, mert az már megadja ezt a jelzést."
},
"notify_icon_color": {
"label": "Értesítés színe",
"doc": "Kiemelőszín ennek a készüléknek az értesítéseihez, így a mosógép, a szárítógép és a mosogatógép egyetlen pillantással megkülönböztethető. Az iOS az ikont, valamint az Élő tevékenység folyamatjelzőjét színezi a Zárolási képernyőn és a Dynamic Islanden. Az Android ugyanazt a színt kapja meg, de hogy mit kezd vele, az a telefontól függ: egyesek az ikont színezik, sok készülék pedig (különösen a Samsung One UI) teljesen figyelmen kívül hagyja. Mindkettő érti a hexa kódot (pl. #03A9F4). Hagyja üresen a platform alapértelmezett beállításához."
}
},
"setting_group": {
@@ -1716,7 +1744,7 @@
"all_statuses": "Minden állapot",
"ambiguous": "Kétértelmű",
"auto_detect": "Automatikus felismerés",
"clear": "Világos",
"clear": "Egyértelmű",
"completed": "Befejezve",
"fine_tuning": "finomhangolás most…",
"force_stopped": "Az erő megállt",
@@ -1766,6 +1794,7 @@
},
"toast": {
"catalog_refreshed": "A közösségi katalógus frissítve",
"contrib_device_changed": "Hozzáadva a közösségi boltba, de a kiválasztott készülék megváltozott, amíg az űrlap nyitva volt. Válassza ki a Beállításokban.",
"access_saved": "A hozzáférés-szabályozás mentve",
"all_wiped": "Minden adat törölve",
"analysis_complete_none": "Az elemzés kész: nincs új javaslat",
@@ -1878,7 +1907,9 @@
"unlabel_done": "{count} ciklus címkéje eltávolítva",
"unlabel_failed": "A címke eltávolítása sikertelen: {error}",
"odometer_saved": "Összesített ciklusszám mentve",
"odometer_save_failed": "Nem sikerült menteni az összesítést: {error}"
"odometer_save_failed": "Nem sikerült menteni az összesítést: {error}",
"store_pick_profile_share": "Válassza ki a profilt, amelybe megosztja",
"remove_failed": "Az eltávolítás sikertelen: {error}"
},
"suggestion": {
"both_agree": "A WashData javasolja",
@@ -1978,6 +2009,7 @@
"summary": "{total} tévesen észlelt ciklus kizárva: {parts}.",
"reason": {
"force_stopped": "kényszerrel leállított",
"user_stopped": "kézzel leállított",
"interrupted": "megszakított",
"incomplete": "befejezetlen",
"noise": "zaj",
@@ -2023,7 +2055,7 @@
"duration_weight": "4. szakasz: mennyire befolyásolja a futási idő egyezése a végső pontszámot (alapértelmezett 0.22)",
"energy_weight": "4. szakasz: mennyire befolyásolja az energia-egyezés a végső pontszámot (alapértelmezett 0.22)",
"profile_match_min_duration_ratio": "1. szakasz: minimális ciklushossz a profil időtartamának törtjeként; alapértelmezett 0.1 (10%)",
"profile_match_max_duration_ratio": "1. szakasz: maximális ciklushossz a profil időtartamának törtjeként; alapértelmezett 1.5 (150%)",
"profile_match_max_duration_ratio": "1. szakasz: maximális ciklushossz a profil időtartamának törtjeként; alapértelmezett 1.8 (180%)",
"keep_min_score": "2. szakasz: minimális pontszám a versenyben maradáshoz; az alapértelmezett 0.1 gyenge egyezéseket is beenged, a következő szakaszok választják ki a legjobbat",
"dtw_blend": "3. szakasz: 0 = csak alappontszám, 1 = csak DTW-pontszám, 0.5 = egyenlő keverék (alapértelmezett)",
"dtw_ensemble_w": "3. szakasz (együttes mód): súly a skálázott-L1 és derivált DTW között; alapértelmezett 0.7 a szintfüggőt részesíti előnyben",
@@ -60,6 +60,7 @@
"fair_fit_tip": "Í meðallagi samræmi við samsvörun - sumar lotur sem eru úthlutaðar þessum prófíl hafa lægri öryggisstig. Merktu fleiri lotur eða skráðu sniðið aftur til að bæta nákvæmni.",
"feedback_requested": "Óskað er eftir umsögn",
"golden_cycle": "Skráð viðmiðunarlota",
"duration_outlier": "{n} lota/lotur með óvenjulega lengd",
"improving": "↗ batnar",
"manual": "(handvirkt valið)",
"needs_review": "Þarfnast endurskoðunar",
@@ -256,7 +257,15 @@
"hist_scan": "Leita að lotum",
"hist_import_n": "Flytja inn {n} lotur",
"hist_goto_cycles": "Sýndu mér loturnar",
"save_total_cycles": "Vista samtölu"
"save_total_cycles": "Vista samtölu",
"clear": "Hreinsa",
"add_brand": "Bæta við vörumerki",
"add_appliance": "Bæta við tæki"
},
"note": {
"off_delay": {
"effective": "Lota sem er lokið bíður í raun {eff} s, ekki {delay} s: Lágm. slökkubil ({gap} s) er lengra og gengur fyrir."
}
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +387,8 @@
"community_store": "Samfélagsverslun",
"online_account": "Samfélagsverslun og nettengdir eiginleikar",
"import_power_history": "Flytja inn aflsögu",
"service_status": "Staða þjónustu"
"service_status": "Staða þjónustu",
"online_account_tab": "Net & samfélag"
},
"health": {
"fair": "Viðunanlegt prófíl",
@@ -523,7 +533,8 @@
"pg_tile_matched_tip": "Lotur sem eru rétt tengdar við þekkt kerfi",
"pg_time_left": "Tími eftir",
"pg_weak_match": "Veik samsvörun",
"phase_name": "Nafn",
"phase_name": "Nafn fasa",
"name": "Nafn",
"phase_timeline": "Tímalína fasa",
"power": "Afl",
"profile": "Prófíll",
@@ -1027,6 +1038,9 @@
"imported_readonly": "Flutt inn úr samfélagsversluninni. Sýnt til viðmiðunar og samsvörunar. Það er ekki talið með í tölfræðinni þinni og ekki hægt að breyta því.",
"merge_no_imports": "Ekki er hægt að sameina innfluttar upptökur við raunverulega lotu. Afveldu þær til að sameina.",
"store_enable_hint": "Virkjaðu nettengda eiginleika í Stillingum til að skoða og flytja inn viðmiðunarlotur samfélagsins.",
"advisory_duration_outlier": "{n} lota/lotur merktar '{name}' eru of langt frá venjulegri lengd kerfisins til að geta nokkurn tíma fengið samsvörun við það (verst: {ratio}x). Þær eru líklega rangt merktar, eða ein upptaka náði tveimur keyrslum. Merktu þær aftur eða skiptu þeim upp svo þær skekki ekki lengur tímaáætlun þessa kerfis.",
"standby_above_stop_title": "Dró enn {idle} W þegar lotunni lauk",
"standby_above_stop_sub": "{n} af síðustu {total} lotum. Slökkviðráð fer ekki af stað fyrr en aflið er komið undir Stöðvunarþröskuld ({stop} W), svo lotur enda of seint eða þarf að stöðva handvirkt.",
"online_intro": "Deildu viðmiðunarlotum með öðrum WashData-notendum og halaðu þeim niður. Allir nettengdir eiginleikar eru valfrjálsir og sjálfgefið óvirkir.",
"store_share_intro": "Hladdu þessari viðmiðunarlotu upp svo aðrir með sama tæki geti notað hana. Hún er yfirfarin áður en hún birtist opinberlega.",
"online_intro_global": "Skoðaðu og deildu viðmiðunarupptökum með öðrum WashData-notendum og staðfestu tækjafærslur. Ein tenging gildir fyrir alla WashData-samþættinguna þína; vörumerki og gerð tækis eru stillt fyrir hvert tæki undir Grunnstillingum. Allir nettengdir eiginleikar eru valfrjálsir og sjálfgefið óvirkir.",
@@ -1087,7 +1101,9 @@
"odometer_intro": "Þessi samtala getur aðeins hækkað. Það að eyða lotuskrá breytir henni ekki, og hún heldur áfram að telja fram yfir hámark vistaðrar sögu, svo að þjónustuáminningar haldist réttar.",
"odometer_correct_hint": "Stilltu þetta ef WashData skráði lotu sem aldrei fór fram, eða ef tækið hafði þegar keyrt lotur áður en WashData var sett upp.",
"no_reminders_set": "Engar þjónustuáminningar hafa enn verið stilltar.",
"toast_program_armed": "Kerfi stillt fyrir næstu lotu: {program}"
"toast_program_armed": "Kerfi stillt fyrir næstu lotu: {program}",
"toast_range_required": "Stilltu gilt Frá/Til-svið.",
"no_profiles_option": "(engir prófílar ennþá, bættu einum við)"
},
"pg_desc": {
"dishwasher_end_spike_quiet_release": "Uppþvottavél: hljóðlátar sekúndur eftir áætlaða lengd áður en biðin eftir útdælingu í lok lotu er losuð",
@@ -1105,7 +1121,7 @@
"duration_weight": "Þrep 4: hversu sterklega samræmi í keyrslulengd hefur áhrif á lokaskorið (sjálfgefið 0,22)",
"energy_weight": "Þrep 4: hversu sterklega samræmi í orkunotkun hefur áhrif á lokaskorið (sjálfgefið 0,22)",
"profile_match_min_duration_ratio": "Þrep 1: stysta lota sem hlutfall af lotulegð sniðs; sjálfgefið 0,1 (10%)",
"profile_match_max_duration_ratio": "Þrep 1: lengsta lota sem hlutfall af lotulegð sniðs; sjálfgefið 1,5 (150%)",
"profile_match_max_duration_ratio": "Þrep 1: lengsta lota sem hlutfall af lotulegð sniðs; sjálfgefið 1,8 (180%)",
"keep_min_score": "Þrep 2: lægsta einkunn til að vera eftir í samsvöruninni; sjálfgefið 0,1 leyfir veika frambjóðendur, síðari þrep velja best",
"dtw_blend": "Þrep 3: 0 = aðeins kjarnaskor, 1 = aðeins DTW-skor, 0,5 = jöfn blanda (sjálfgefið)",
"dtw_ensemble_w": "Þrep 3 (ensemble-hamur): þunginn á skaluðum L1 gagnvart afleiðu-DTW; sjálfgefið 0,7 hlynnar að stiga-meðvitund",
@@ -1446,8 +1462,8 @@
"label": "Losunarminni-seinkun"
},
"notify_unload_repeat": {
"doc": "Heldur áfram að senda losunaráminninguna aftur á \"Losunarminni-seinkun\" mínútna fresti þar til þú opnar hurðina eða ýtir á \"Hætta að minna\" í tilkynningunni. Krefst Dyraskynjareiningar (áminningin sjálf gerir það). Hunsa-hnappurinn virkar á tilkynningum úr Home Assistant fylgiforritinu (farsíma).",
"label": "Endurtaka þar til hurðin opnast"
"doc": "Heldur áfram að senda losunaráminninguna aftur á \"Losunarminni-seinkun\" mínútna fresti þar til losunin er staðfest eða þú ýtir á \"Hætta að minna\" í tilkynningunni. Krefst leiðar til að staðfesta losunina: Dyraskynjareiningar, Losunarstaðfestingareiningar eða valkostsins \"Staðfesta losun handvirkt\" hér að neðan. Hunsa-hnappurinn virkar á tilkynningum úr Home Assistant fylgiforritinu (farsíma).",
"label": "Endurtaka þar til losað"
},
"off_delay": {
"doc": "Tími til að bíða eftir að kraftur minnkar áður en hringrásinni er lokið. Ef straumur fer aftur innan þessa glugga heldur hringrásin óaðfinnanlega áfram - þetta brúar hlé á milli þvottastiga. Uppþvottavélar eru með langa þurrkunarfasa (slökkt á slökkt í 20-60 mín) þannig að slökkvunartöfin verður að fara yfir það til að halda öllum þvotti+þurrkum sem einni lotu.",
@@ -1655,6 +1671,18 @@
"energy_price_dynamic": {
"label": "Tímaveginn kostnaður",
"doc": "Reiknar kostnað hverrar lotu á því verði sem gilti á hverri stundu meðan hún keyrði, í stað eins verðs sem gilti þegar henni lauk. Á aðeins við um verðeiningu (fast verð getur ekki breyst). Þarf enga auka uppsetningu: verðinu er fylgt í rauntíma meðan lotan keyrir, og „Vinna úr sögu“ undir Greiningu endurreiknar kostnað eldri lota út frá recorder. Af = klassíska hegðunin, eitt verð fryst við lokin."
},
"unload_confirm_entity": {
"label": "Losunarstaðfestingareining",
"doc": "Valfrjáls eining þar sem virkjun merkir að þvotturinn hafi verið tekinn út, fyrir vélar sem geta ekki haft hurðarskynjara. Allar gerðir virka: Zigbee- eða NFC-hnappur, input_button hjálpareining, hreyfiskynjari fyrir framan vélina, sena. Hver stöðubreyting telst sem staðfesting, nema þegar slökkt er á einingunni eða hún verður ótiltæk. Að stilla þetta kveikir einnig á losunaráminningunni á tæki án Dyraskynjareiningar."
},
"unload_track_without_door": {
"label": "Staðfesta losun handvirkt",
"doc": "Keyrir losunaráminninguna á tæki án Dyraskynjareiningar og án Losunarstaðfestingareiningar, og þú staðfestir losunina þess í stað úr þinni eigin sjálfvirkni: ýttu á hnappaeininguna \"Merkja sem losað\" eða kallaðu á aðgerðina ha_washdata.mark_unloaded. Hunsað þegar Dyraskynjareining er stillt, því hún gefur merkið nú þegar."
},
"notify_icon_color": {
"label": "Tilkynningalitur",
"doc": "Áherslulitur fyrir tilkynningar frá þessu tæki, svo að þvottavél, þurrkari og uppþvottavél þekkist í sjónhendingu. iOS litar táknið og framvindustikuna í Live Activity á lásskjánum og í Dynamic Island. Android fær sama litinn sendan, en hvað síminn gerir við hann er undir honum komið: sum tæki lita táknið og mörg (einkum Samsung One UI) hunsa hann alveg. Bæði kerfin skilja hex-gildi (t.d. #03A9F4). Skildu eftir autt fyrir sjálfgefinn lit kerfisins."
}
},
"setting_group": {
@@ -1748,7 +1776,7 @@
"all_statuses": "Allar stöður",
"ambiguous": "Óljós",
"auto_detect": "Sjálfvirk skynjun",
"clear": "Hreinsa",
"clear": "Ótvíræð",
"completed": "Lokið",
"fine_tuning": "fínstilla núna…",
"force_stopped": "Þvingað stöðvað",
@@ -1867,6 +1895,7 @@
"summary": "Útilokaði {total} ranggreindar lotur: {parts}.",
"reason": {
"force_stopped": "þvingað stöðvaðar",
"user_stopped": "stöðvaðar handvirkt",
"interrupted": "rofnar",
"incomplete": "ókláraðar",
"noise": "suð",
@@ -2021,7 +2050,10 @@
"unlabel_done": "Fjarlægði merkingu af {count} lotu(m)",
"unlabel_failed": "Fjarlæging merkingar mistókst: {error}",
"odometer_saved": "Samtala lotna vistuð",
"odometer_save_failed": "Ekki tókst að vista samtöluna: {error}"
"odometer_save_failed": "Ekki tókst að vista samtöluna: {error}",
"contrib_device_changed": "Bætt við samfélagsverslunina, en skipt var um tæki meðan eyðublaðið var opið. Veldu það í Stillingum.",
"store_pick_profile_share": "Veldu prófíl til að deila",
"remove_failed": "Fjarlæging mistókst: {error}"
},
"trend": {
"down": "Stefnir niður",
@@ -35,7 +35,8 @@
"not_importable": "n/d qui",
"exists": "esiste già",
"backfilled_tip": "Rilevato in uno storico di potenza importato. Influenza solo la corrispondenza dei programmi, non conteggiato nelle statistiche.",
"armed": "(si applica al prossimo ciclo)"
"armed": "(si applica al prossimo ciclo)",
"duration_outlier": "{n} ciclo/i di durata anomala"
},
"btn": {
"reset_zoom": "Reimposta zoom",
@@ -56,7 +57,7 @@
"auto_label_cycles_tip": "Assegna automaticamente nomi di profilo ai cicli senza etichetta la cui confidenza di corrispondenza supera la soglia",
"cancel": "Cancellare",
"clear_debug": "Cancella dati di debug",
"clear_splits": "Chiaro",
"clear_splits": "Cancella",
"close": "Chiudi",
"compare": "Confrontare",
"confirm": "Confermare",
@@ -208,7 +209,10 @@
"hist_scan": "Cerca cicli",
"hist_import_n": "Importa {n} cicli",
"hist_goto_cycles": "Mostrami i cicli",
"save_total_cycles": "Salva totale"
"save_total_cycles": "Salva totale",
"clear": "Cancella",
"add_brand": "Aggiungi marca",
"add_appliance": "Aggiungi elettrodomestico"
},
"conflict": {
"anti_wrinkle_exit": {
@@ -330,7 +334,8 @@
"community_store": "Store della community",
"online_account": "Store della community e funzionalità online",
"import_power_history": "Importa lo storico di potenza",
"service_status": "Stato della manutenzione"
"service_status": "Stato della manutenzione",
"online_account_tab": "Online e community"
},
"health": {
"fair": "Qualità profilo accettabile",
@@ -470,7 +475,8 @@
"pg_tile_matched_tip": "Cicli correttamente attribuiti a un programma noto",
"pg_time_left": "Tempo rimanente",
"pg_weak_match": "Abbinamento debole",
"phase_name": "Nome",
"phase_name": "Nome della fase",
"name": "Nome",
"phase_timeline": "Cronologia fasi",
"power": "Energia",
"profile": "Profilo",
@@ -1039,7 +1045,12 @@
"odometer_intro": "Questo totale può solo aumentare. L'eliminazione del record di un ciclo non lo modifica e continua a contare oltre il limite dello storico conservato, così i promemoria di servizio restano corretti.",
"odometer_correct_hint": "Imposta questo valore se WashData ha registrato un ciclo che non è mai avvenuto, oppure se l'elettrodomestico aveva già eseguito dei cicli prima dell'installazione di WashData.",
"no_reminders_set": "Non è ancora impostato alcun promemoria di servizio.",
"toast_program_armed": "Programma preimpostato per il prossimo ciclo: {program}"
"toast_program_armed": "Programma preimpostato per il prossimo ciclo: {program}",
"standby_above_stop_title": "Assorbe ancora {idle} W alla fine del ciclo",
"standby_above_stop_sub": "{n} degli ultimi {total} cicli. Il Ritardo spegnimento parte solo quando la potenza scende sotto la Soglia di arresto ({stop} W), quindi i cicli finiscono in ritardo o vanno fermati a mano.",
"advisory_duration_outlier": "{n} ciclo/i etichettati come '{name}' si discostano troppo dalla sua durata abituale per potergli mai corrispondere (caso peggiore: {ratio}x). Probabilmente sono etichettati male, oppure una registrazione ha catturato due esecuzioni. Rietichettali o dividili, così smettono di falsare la stima dei tempi di questo programma.",
"toast_range_required": "Imposta un intervallo Da/A valido",
"no_profiles_option": "(nessun profilo, aggiungine uno)"
},
"pg_desc": {
"dishwasher_end_spike_quiet_release": "Lavastoviglie: secondi di silenzio dopo la durata prevista prima che venga rilasciata l'attesa dello scarico di fine ciclo",
@@ -1063,7 +1074,7 @@
"duration_weight": "Fase 4: quanto la concordanza di durata influisce sul punteggio finale (predefinito 0,22)",
"energy_weight": "Fase 4: quanto la concordanza di energia influisce sul punteggio finale (predefinito 0,22)",
"profile_match_min_duration_ratio": "Fase 1: durata minima del ciclo come frazione della durata del profilo; predefinito 0,1 (10%)",
"profile_match_max_duration_ratio": "Fase 1: durata massima del ciclo come frazione della durata del profilo; predefinito 1,5 (150%)",
"profile_match_max_duration_ratio": "Fase 1: durata massima del ciclo come frazione della durata del profilo; predefinito 1,8 (180%)",
"keep_min_score": "Fase 2: punteggio minimo per restare in gara; il predefinito 0,1 ammette candidati deboli, le fasi successive scelgono il migliore",
"dtw_blend": "Fase 3: 0 = solo punteggio centrale, 1 = solo punteggio DTW, 0,5 = mix equilibrato (predefinito)",
"dtw_ensemble_w": "Fase 3 (modalità ensemble): peso su L1 scalato rispetto a DTW derivato; il predefinito 0,7 favorisce la sensibilità al livello",
@@ -1398,8 +1409,8 @@
"label": "Ritardo promemoria scarico"
},
"notify_unload_repeat": {
"doc": "Continua a inviare il promemoria di scarico ogni \"Ritardo promemoria scarico\" minuti finché non apri la porta o tocchi \"Interrompi promemoria\" nella notifica. Richiede un'Entità sensore porta (il promemoria stesso la richiede). Il pulsante di chiusura funziona sulle notifiche dell'app complementare (mobile) di Home Assistant.",
"label": "Ripeti finché la porta non si apre"
"doc": "Continua a inviare il promemoria di scarico ogni \"Ritardo promemoria scarico\" minuti finché lo scarico non viene confermato o non tocchi \"Interrompi promemoria\" nella notifica. Richiede un modo per confermare lo scarico: un'Entità sensore porta, un'Entità di conferma scarico o l'opzione \"Conferma scarico manualmente\" qui sotto. Il pulsante di chiusura funziona sulle notifiche dell'app complementare (mobile) di Home Assistant.",
"label": "Ripeti fino alla conferma di scarico"
},
"off_delay": {
"doc": "È tempo di attendere dopo l'interruzione della corrente prima di dichiarare terminato il ciclo. Se l'alimentazione viene ripristinata entro questo intervallo, il ciclo continua senza interruzioni, consentendo di colmare le pause tra le fasi di lavaggio. Le lavastoviglie hanno fasi di asciugatura lunghe (spegnimento per 20-60 minuti), quindi il ritardo di spegnimento deve superare quello per mantenere l'intero ciclo di lavaggio+asciugatura come un ciclo.",
@@ -1607,6 +1618,18 @@
"energy_price_dynamic": {
"label": "Costo ponderato nel tempo",
"doc": "Addebita ogni ciclo al prezzo in vigore in ogni momento in cui è stato eseguito, invece dell'unico prezzo valido al termine. Vale solo per un'entità di prezzo (un prezzo statico non può variare). Non serve alcuna configurazione aggiuntiva: il prezzo viene seguito in tempo reale mentre il ciclo è in corso, e Elabora storico, in Diagnostica, ricalcola il costo dei cicli più vecchi a partire dal recorder. Disattivato = il comportamento classico, un unico prezzo fissato alla fine."
},
"unload_confirm_entity": {
"label": "Entità di conferma scarico",
"doc": "Entità facoltativa la cui attivazione indica che il carico è stato tolto, per le macchine che non possono avere un sensore porta. Va bene qualsiasi tipo: un pulsante Zigbee o NFC, un helper input_button, un sensore di movimento davanti alla macchina, una scena. Ogni cambio di stato conta come conferma, tranne lo spegnimento e il passaggio a non disponibile. Impostarla abilita anche il promemoria di scarico su un dispositivo senza Entità sensore porta."
},
"unload_track_without_door": {
"label": "Conferma scarico manualmente",
"doc": "Esegue il promemoria di scarico su un dispositivo senza Entità sensore porta e senza Entità di conferma scarico, e lo scarico lo confermi tu dalla tua automazione: premi l'entità pulsante \"Segna come scaricato\" oppure chiama il servizio ha_washdata.mark_unloaded. Ignorato quando è impostata un'Entità sensore porta, che fornisce già il segnale."
},
"notify_icon_color": {
"label": "Colore notifica",
"doc": "Colore d'accento per le notifiche di questo elettrodomestico, così da distinguere a colpo d'occhio una lavatrice, un'asciugatrice e una lavastoviglie. iOS colora l'icona e la barra di avanzamento dell'Attività in tempo reale sulla schermata di blocco e nella Dynamic Island. Ad Android viene inviato lo stesso colore, ma ciò che ne fa dipende dal telefono: alcuni colorano l'icona e molti (in particolare Samsung One UI) lo ignorano del tutto. Il formato esadecimale (es. #03A9F4) è compreso da entrambi. Lascia vuoto per l'impostazione predefinita della piattaforma."
}
},
"setting_group": {
@@ -1700,7 +1723,7 @@
"all_statuses": "Tutti gli stati",
"ambiguous": "Ambiguo",
"auto_detect": "Rilevamento automatico",
"clear": "Chiaro",
"clear": "Univoco",
"completed": "Completato",
"fine_tuning": "messa a punto ora...",
"force_stopped": "La forza si è fermata",
@@ -1839,6 +1862,7 @@
"store_share_failed": "Condivisione non riuscita: {error}",
"store_shared": "Condiviso nello store della community, in attesa di revisione.",
"appliance_added": "Elettrodomestico aggiunto, in attesa di approvazione",
"contrib_device_changed": "Aggiunto allo store della community, ma il dispositivo selezionato è cambiato mentre il modulo era aperto. Selezionalo nelle Impostazioni.",
"thanks_confirming": "Grazie per la conferma",
"device_approved": "Approvato dalla community",
"rating_saved": "Valutazione della qualità salvata",
@@ -1862,7 +1886,9 @@
"unlabel_done": "Etichetta rimossa da {count} cicli",
"unlabel_failed": "Rimozione dell'etichetta non riuscita: {error}",
"odometer_saved": "Totale dei cicli salvato",
"odometer_save_failed": "Impossibile salvare il totale: {error}"
"odometer_save_failed": "Impossibile salvare il totale: {error}",
"store_pick_profile_share": "Scegli un profilo in cui condividere",
"remove_failed": "Rimozione non riuscita: {error}"
},
"suggestion": {
"both_agree": "WashData consiglia",
@@ -1962,6 +1988,7 @@
"summary": "Esclusi {total} cicli rilevati male: {parts}.",
"reason": {
"force_stopped": "arrestati forzatamente",
"user_stopped": "arrestati manualmente",
"interrupted": "interrotti",
"incomplete": "incompleti",
"noise": "rumore",
@@ -2187,5 +2214,10 @@
"hold_action": "Azione alla pressione prolungata",
"double_tap_action": "Azione al doppio tocco"
}
},
"note": {
"off_delay": {
"effective": "Un ciclo terminato attende in realtà {eff} s, non {delay} s: l'Intervallo minimo di spegnimento ({gap} s) è più lungo e ha la precedenza."
}
}
}
@@ -60,6 +60,7 @@
"fair_fit_tip": "中程度の一致一貫性 - このプロファイルに割り当てられた一部のサイクルの信頼スコアが低くなります。精度を向上させるには、さらに多くのサイクルにラベルを付けるか、プロファイルを再記録します。",
"feedback_requested": "フィードバックが必要です",
"golden_cycle": "記録された基準サイクル",
"duration_outlier": "長さが異常なサイクル {n} 件",
"improving": "↗ 改善中",
"manual": "(手動で選択)",
"needs_review": "見直しが必要",
@@ -256,7 +257,15 @@
"hist_scan": "サイクルを検索",
"hist_import_n": "{n} サイクルをインポート",
"hist_goto_cycles": "サイクルを表示",
"save_total_cycles": "累計を保存"
"save_total_cycles": "累計を保存",
"clear": "クリア",
"add_brand": "ブランドを追加",
"add_appliance": "家電を追加"
},
"note": {
"off_delay": {
"effective": "終了したサイクルが実際に待機するのは {delay} 秒ではなく {eff} 秒です。最小オフギャップ ({gap} 秒) の方が長く、そちらが優先されます。"
}
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +387,8 @@
"community_store": "コミュニティストア",
"online_account": "コミュニティストアとオンライン機能",
"import_power_history": "電力履歴のインポート",
"service_status": "メンテナンス状況"
"service_status": "メンテナンス状況",
"online_account_tab": "オンラインとコミュニティ"
},
"health": {
"fair": "プロファイル品質: 普通",
@@ -518,7 +528,8 @@
"pg_tile_matched_tip": "既知のプログラムに正しく割り当てられたサイクル",
"pg_time_left": "残り時間",
"pg_weak_match": "弱いマッチ",
"phase_name": "名前",
"phase_name": "フェーズ名",
"name": "名前",
"phase_timeline": "フェーズタイムライン",
"power": "電力",
"profile": "プロファイル",
@@ -1027,6 +1038,9 @@
"imported_readonly": "コミュニティストアからインポートしました。参照とマッチングのために表示されます。統計には含まれず、編集できません。",
"merge_no_imports": "インポートした録音は実際のサイクルに統合できません。統合するには選択を解除してください。",
"store_enable_hint": "設定でオンライン機能を有効にすると、コミュニティの基準サイクルを閲覧・インポートできます。",
"advisory_duration_outlier": "'{name}' とラベル付けされたサイクル {n} 件は、このプログラムの通常の長さから離れすぎており、マッチングされることはありません (最大 {ratio}x)。ラベルが間違っているか、1 回の記録に 2 回分の運転が含まれている可能性があります。ラベルを付け直すか分割して、このプログラムの時間推定がゆがまないようにしてください。",
"standby_above_stop_title": "サイクル終了時にまだ {idle} W を消費しています",
"standby_above_stop_sub": "直近 {total} サイクル中 {n} 件。オフ遅延は電力が停止しきい値 ({stop} W) を下回ってから始まるため、サイクルの終了が遅れたり、手動で停止する必要が生じたりします。",
"online_intro": "他の WashData ユーザーと基準サイクルを共有・ダウンロードできます。すべてのオンライン機能はオプトインで、デフォルトではオフです。",
"store_share_intro": "この基準サイクルをアップロードすると、同じ家電を持つ他のユーザーが利用できます。公開前に審査されます。",
"online_intro_global": "他の WashData ユーザーと基準録音を閲覧・共有し、家電エントリを確認できます。1 つの接続が WashData 統合全体に適用されます。家電のブランドとモデルは「基本設定」でデバイスごとに設定します。すべてのオンライン機能はオプトインで、デフォルトではオフです。",
@@ -1087,7 +1101,9 @@
"odometer_intro": "この累計は増える一方です。サイクルの記録を削除しても変わらず、保存される履歴の上限を超えてもカウントを続けるため、サービスリマインダーは正確なままです。",
"odometer_correct_hint": "WashData が実際には行われていない稼働を記録した場合や、WashData を導入する前に家電がすでにサイクルを実行していた場合は、この値を設定してください。",
"no_reminders_set": "サービスリマインダーはまだ設定されていません。",
"toast_program_armed": "次のサイクル用にプログラムを設定しました: {program}"
"toast_program_armed": "次のサイクル用にプログラムを設定しました: {program}",
"toast_range_required": "有効な開始/終了の範囲を設定してください。",
"no_profiles_option": "(プロファイルなし、追加してください)"
},
"pg_desc": {
"completion_min_seconds": "実サイクルとみなす最短の稼働時間",
@@ -1108,7 +1124,7 @@
"duration_weight": "ステージ 4: 運転時間の一致が最終スコアにどれだけ影響するか(デフォルト 0.22)",
"energy_weight": "ステージ 4: エネルギーの一致が最終スコアにどれだけ影響するか(デフォルト 0.22)",
"profile_match_min_duration_ratio": "ステージ 1: プロファイル所要時間に対するサイクルの最短許容比率; デフォルト 0.1 (10%)",
"profile_match_max_duration_ratio": "ステージ 1: プロファイル所要時間に対するサイクルの最長許容比率; デフォルト 1.5 (150%)",
"profile_match_max_duration_ratio": "ステージ 1: プロファイル所要時間に対するサイクルの最長許容比率; デフォルト 1.8 (180%)",
"keep_min_score": "ステージ 2: 競合に残るための最低スコア; デフォルト 0.1 は弱い候補も許可し、後のステージが最良を選ぶ",
"dtw_blend": "ステージ 3: 0 = コアスコアのみ、1 = DTW スコアのみ、0.5 = 等割合ブレンド(デフォルト)",
"dtw_ensemble_w": "ステージ 3 (アンサンブルモード): スケール付き-L1 と微分 DTW の重み; デフォルト 0.7 はレベル考慮を優先",
@@ -1434,8 +1450,8 @@
"label": "取り出し通知遅延"
},
"notify_unload_repeat": {
"doc": "ドアを開くか、通知の \"リマインダーを停止\" をタップするまで、\"取り出し通知遅延\" 分ごとに取り出しリマインダーを再送信し続けます。ドアセンサーエンティティが必要です(リマインダー自体が必要とします)。解除ボタンは Home Assistant コンパニオンアプリ(モバイル)の通知で機能します。",
"label": "ドアが開くまで繰り返す"
"doc": "取り出しが確認されるか、通知の \"リマインダーを停止\" をタップするまで、\"取り出し通知遅延\" 分ごとに取り出しリマインダーを再送信し続けます。取り出しを確認する手段が必要です: ドアセンサーエンティティ、取り出し確認エンティティ、または下の \"取り出しを手動で確認\" オプション。解除ボタンは Home Assistant コンパニオンアプリ(モバイル)の通知で機能します。",
"label": "取り出すまで繰り返す"
},
"off_delay": {
"doc": "電力が低下した後、サイクルの終了を宣言するまで待機する時間。このウィンドウ内で電力が再開されると、サイクルはシームレスに継続されます。これにより、洗浄段階の間で一時停止が生じます。食器洗い機には長い乾燥段階 (20 ~ 60 分間電源がオフになる) があるため、洗浄と乾燥全体を 1 サイクルとして維持するには、オフディレーをそれを超える必要があります。",
@@ -1655,6 +1671,18 @@
"curve_preroll_seconds": {
"label": "曲線のプリロール",
"doc": "中断された開始試行の測定値を、サイクルの曲線の先頭に何秒ぶん取り込めるかを指定します。サイクルの曲線は通常、最終的に成功した開始試行から始まります。そのため、プログラム選択、ドアロック、最初の給水などで数回試してから安定するマシンでは、実際の動作の最初の数分がどの曲線からも欠落します。お使いのマシンで最も長い立ち上がりより少し大きい値を設定してください(多くの場合 300 秒あれば十分です)。0 にすると無効になります。有効にすると記録される開始時刻が前倒しになるため、変更の前後に記録されたサイクルは同じプログラムでも所要時間が異なります。古いサイクルが入れ替わるまで、学習された平均値はずれ続けます。"
},
"unload_confirm_entity": {
"label": "取り出し確認エンティティ",
"doc": "ドアセンサーを取り付けられない機器向けに、作動したら中身を取り出したとみなすエンティティ(任意)。種類は問いません: Zigbee や NFC のボタン、input_button ヘルパー、機器の前に置いたモーションセンサー、シーンなど。オフになった場合と利用不可になった場合を除き、すべての状態変化が確認とみなされます。これを設定すると、ドアセンサーエンティティのないデバイスでも取り出しリマインダーが有効になります。"
},
"unload_track_without_door": {
"label": "取り出しを手動で確認",
"doc": "ドアセンサーエンティティも取り出し確認エンティティもないデバイスで取り出しリマインダーを動作させ、代わりに自作のオートメーションから取り出しを確認します: \"取り出し済みとしてマーク\" ボタンエンティティを押すか、ha_washdata.mark_unloaded サービスを呼び出します。ドアセンサーエンティティが設定されている場合は、すでに信号が得られるため無視されます。"
},
"notify_icon_color": {
"label": "通知の色",
"doc": "この家電の通知に使うアクセントカラー。洗濯機、乾燥機、食器洗い機をひと目で見分けられます。iOS はアイコンと、ロック画面や Dynamic Island に表示されるライブアクティビティの進行状況バーに色を付けます。Android にも同じ色が送られますが、それをどう扱うかは端末次第で、アイコンに色を付けるものもあれば、まったく無視するもの(特に Samsung One UI)も多くあります。16 進数(例: #03A9F4)はどちらでも使えます。プラットフォームのデフォルトを使う場合は空欄のままにします。"
}
},
"setting_group": {
@@ -1748,7 +1776,7 @@
"all_statuses": "すべてのステータス",
"ambiguous": "あいまい",
"auto_detect": "自動検出",
"clear": "クリア",
"clear": "明確",
"completed": "完了",
"fine_tuning": "今微調整中…",
"force_stopped": "強制停止",
@@ -1910,7 +1938,10 @@
"unlabel_done": "{count} 件のサイクルのラベルを解除しました",
"unlabel_failed": "ラベルの解除に失敗しました: {error}",
"odometer_saved": "累計サイクル数を保存しました",
"odometer_save_failed": "累計を保存できませんでした: {error}"
"odometer_save_failed": "累計を保存できませんでした: {error}",
"contrib_device_changed": "コミュニティストアに追加しましたが、入力フォームを開いている間にデバイスが変更されました。設定で選択してください。",
"store_pick_profile_share": "共有先のプロファイルを選択してください",
"remove_failed": "削除に失敗しました: {error}"
},
"suggestion": {
"both_agree": "WashDataの推奨",
@@ -2010,6 +2041,7 @@
"summary": "{total} 件の誤検出サイクルを除外しました: {parts}。",
"reason": {
"force_stopped": "強制停止",
"user_stopped": "手動で停止",
"interrupted": "中断",
"incomplete": "未完了",
"noise": "ノイズ",
@@ -60,6 +60,7 @@
"fair_fit_tip": "중간 수준의 일치 일관성 - 이 프로필에 할당된 일부 주기의 신뢰도 점수가 낮습니다. 정확성을 높이려면 더 많은 주기에 라벨을 붙이거나 프로필을 다시 기록하십시오.",
"feedback_requested": "피드백 요청됨",
"golden_cycle": "기록된 참조 사이클",
"duration_outlier": "길이가 이상한 사이클 {n}개",
"improving": "↗ 개선",
"manual": "(수동으로 선택)",
"needs_review": "검토 필요",
@@ -256,7 +257,15 @@
"hist_scan": "사이클 검색",
"hist_import_n": "{n}개 사이클 가져오기",
"hist_goto_cycles": "사이클 보기",
"save_total_cycles": "누적 값 저장"
"save_total_cycles": "누적 값 저장",
"clear": "지우기",
"add_brand": "브랜드 추가",
"add_appliance": "가전제품 추가"
},
"note": {
"off_delay": {
"effective": "완료된 사이클이 실제로 기다리는 시간은 {delay}초가 아니라 {eff}초입니다. 최소 오프 간격({gap}초)이 더 길어 우선 적용됩니다."
}
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +387,8 @@
"community_store": "커뮤니티 스토어",
"online_account": "커뮤니티 스토어 및 온라인 기능",
"import_power_history": "전력 이력 가져오기",
"service_status": "유지보수 상태"
"service_status": "유지보수 상태",
"online_account_tab": "온라인 및 커뮤니티"
},
"health": {
"fair": "프로파일 품질: 보통",
@@ -518,7 +528,8 @@
"pg_tile_matched_tip": "알려진 프로그램에 올바르게 귀속된 사이클",
"pg_time_left": "남은 시간",
"pg_weak_match": "약한 매칭",
"phase_name": "이름",
"phase_name": "단계 이름",
"name": "이름",
"phase_timeline": "단계 타임라인",
"power": "전력",
"profile": "프로필",
@@ -1027,6 +1038,9 @@
"imported_readonly": "커뮤니티 스토어에서 가져왔습니다. 참조 및 매칭용으로 표시됩니다. 통계에는 포함되지 않으며 편집할 수 없습니다.",
"merge_no_imports": "가져온 녹음은 실제 사이클에 병합할 수 없습니다. 병합하려면 선택을 해제하세요.",
"store_enable_hint": "설정에서 온라인 기능을 활성화하면 커뮤니티 참조 주기를 탐색하고 가져올 수 있습니다.",
"advisory_duration_outlier": "'{name}'(으)로 라벨이 지정된 사이클 {n}개는 이 프로그램의 일반적인 길이에서 너무 멀어 매칭될 수 없습니다(최대 {ratio}x). 라벨이 잘못되었거나, 한 번의 기록에 두 번의 운전이 담겼을 수 있습니다. 라벨을 다시 지정하거나 분리해서 이 프로그램의 시간 추정이 왜곡되지 않도록 하세요.",
"standby_above_stop_title": "사이클이 끝났을 때 여전히 {idle} W를 사용 중입니다",
"standby_above_stop_sub": "최근 {total}개 사이클 중 {n}개입니다. 오프 지연은 전력이 중지 임계값({stop} W) 아래로 내려간 뒤에야 시작되므로, 사이클이 늦게 끝나거나 수동으로 정지해야 합니다.",
"online_intro": "다른 WashData 사용자와 참조 주기를 공유하고 다운로드하세요. 모든 온라인 기능은 선택 사항이며 기본적으로 꺼져 있습니다.",
"store_share_intro": "이 참조 주기를 업로드하면 같은 가전제품을 사용하는 다른 사용자가 이용할 수 있습니다. 공개되기 전에 검토를 거칩니다.",
"online_intro_global": "다른 WashData 사용자와 참조 녹음을 탐색하고 공유하며 가전제품 항목을 확인하세요. 하나의 연결이 전체 WashData 통합에 적용됩니다. 가전제품 브랜드와 모델은 '기본 구성'에서 기기별로 설정합니다. 모든 온라인 기능은 선택 사항이며 기본적으로 꺼져 있습니다.",
@@ -1087,7 +1101,9 @@
"odometer_intro": "이 누적 값은 줄어들지 않습니다. 사이클 기록을 삭제해도 값이 바뀌지 않으며, 저장되는 기록 한도를 넘어서도 계속 집계되므로 서비스 알림이 정확하게 유지됩니다.",
"odometer_correct_hint": "WashData가 실제로 일어나지 않은 작동을 기록했거나, WashData를 설치하기 전에 기기가 이미 사이클을 실행한 적이 있다면 이 값을 설정하세요.",
"no_reminders_set": "설정된 서비스 알림이 아직 없습니다.",
"toast_program_armed": "다음 사이클용 프로그램이 설정됨: {program}"
"toast_program_armed": "다음 사이클용 프로그램이 설정됨: {program}",
"toast_range_required": "유효한 시작/끝 범위를 설정하세요.",
"no_profiles_option": "(프로필 없음, 하나 추가하세요)"
},
"pg_desc": {
"completion_min_seconds": "실제 사이클로 인정되는 최단 실행",
@@ -1108,7 +1124,7 @@
"duration_weight": "Stage 4: 실행 시간 일치가 최종 점수에 미치는 영향(기본값 0.22)",
"energy_weight": "Stage 4: 에너지 일치가 최종 점수에 미치는 영향(기본값 0.22)",
"profile_match_min_duration_ratio": "Stage 1: 프로파일 지속 시간의 분수로서 최소 사이클 길이; 기본값 0.1 (10%)",
"profile_match_max_duration_ratio": "Stage 1: 프로파일 지속 시간의 분수로서 최대 사이클 길이; 기본값 1.5 (150%)",
"profile_match_max_duration_ratio": "Stage 1: 프로파일 지속 시간의 분수로서 최대 사이클 길이; 기본값 1.8 (180%)",
"keep_min_score": "Stage 2: 경쟁에 남기 위한 최소 점수; 기본값 0.1은 약한 후보도 허용하며, 이후 단계에서 최선을 선택",
"dtw_blend": "Stage 3: 0 = 핵심 점수만, 1 = DTW 점수만, 0.5 = 동등 혼합(기본값)",
"dtw_ensemble_w": "Stage 3 (앙상블 모드): 스케일 L1 대 도함수 DTW 가중치; 기본값 0.7은 수준 인식 방식을 선호",
@@ -1434,8 +1450,8 @@
"label": "꺼내기 알림 지연"
},
"notify_unload_repeat": {
"doc": "문을 열거나 알림에서 \"알림 중지\"를 누를 때까지 \"꺼내기 알림 지연\" 분마다 꺼내기 알림을 계속 다시 보냅니다. 문 센서 엔티티가 필요합니다(알림 자체가 필요로 합니다). 해제 버튼은 Home Assistant 컴패니언 앱(모바일) 알림에서 작동합니다.",
"label": "문이 열릴 때까지 반복"
"doc": "꺼내기가 확인되거나 알림에서 \"알림 중지\"를 누를 때까지 \"꺼내기 알림 지연\" 분마다 꺼내기 알림을 계속 다시 보냅니다. 꺼내기를 확인할 방법이 필요합니다: 문 센서 엔티티, 꺼내기 확인 엔티티 또는 아래의 \"꺼내기 수동 확인\" 옵션. 해제 버튼은 Home Assistant 컴패니언 앱(모바일) 알림에서 작동합니다.",
"label": "꺼낼 때까지 반복"
},
"off_delay": {
"doc": "사이클 완료를 선언하기 전에 전원이 꺼진 후 기다리는 시간입니다. 이 창 내에서 전원이 재개되면 주기가 원활하게 계속됩니다. 이로 인해 세탁 단계 사이가 일시 중지됩니다. 식기세척기는 건조 단계가 길기 때문에(20~60분 동안 전원 꺼짐) 전체 세탁+건조를 하나의 주기로 유지하려면 꺼짐 지연이 이를 초과해야 합니다.",
@@ -1655,6 +1671,18 @@
"curve_preroll_seconds": {
"label": "곡선 프리롤",
"doc": "중단된 시작 시도에서 얻은 측정값을 사이클 곡선의 앞부분에 몇 초까지 포함할 수 있는지 지정합니다. 사이클 곡선은 보통 최종적으로 성공한 시작 시도부터 시작하므로, 프로그램 선택, 도어 잠금, 첫 급수처럼 몇 번 시도한 뒤에야 안정되는 기계에서는 실제 동작의 첫 몇 분이 모든 곡선에서 빠집니다. 기계에서 가장 긴 준비 구간보다 조금 큰 값으로 설정하십시오(대부분 300초면 충분합니다). 0이면 꺼집니다. 이 기능을 켜면 기록되는 시작 시점이 앞당겨지므로, 변경 전후에 기록된 사이클은 같은 프로그램이라도 소요 시간이 달라집니다. 오래된 사이클이 밀려날 때까지 학습된 평균값은 어긋난 상태로 유지됩니다."
},
"unload_confirm_entity": {
"label": "꺼내기 확인 엔티티",
"doc": "문 센서를 달 수 없는 기기를 위해, 작동하면 내용물을 꺼낸 것으로 간주할 엔티티(선택 사항). 종류는 상관없습니다: Zigbee 또는 NFC 버튼, input_button 도우미, 기기 앞에 둔 동작 감지 센서, 장면 등. 꺼짐과 사용 불가로 바뀌는 경우를 제외한 모든 상태 변화가 확인으로 인정됩니다. 이 항목을 설정하면 문 센서 엔티티가 없는 장치에서도 꺼내기 알림이 활성화됩니다."
},
"unload_track_without_door": {
"label": "꺼내기 수동 확인",
"doc": "문 센서 엔티티와 꺼내기 확인 엔티티가 모두 없는 장치에서도 꺼내기 알림을 실행하고, 대신 직접 만든 자동화에서 꺼내기를 확인합니다: \"꺼냄으로 표시\" 버튼 엔티티를 누르거나 ha_washdata.mark_unloaded 서비스를 호출하세요. 문 센서 엔티티가 설정되어 있으면 이미 신호가 있으므로 무시됩니다."
},
"notify_icon_color": {
"label": "알림 색상",
"doc": "이 가전제품의 알림에 사용할 강조 색상으로, 세탁기와 건조기, 식기세척기를 한눈에 구분할 수 있습니다. iOS는 아이콘과 잠금 화면 및 Dynamic Island에 표시되는 실시간 현황의 진행률 표시줄에 색을 입힙니다. Android에도 같은 색상이 전달되지만 이를 어떻게 처리할지는 휴대폰에 달려 있습니다. 아이콘에 색을 입히는 기기도 있고, 아예 무시하는 기기도 많습니다(특히 Samsung One UI). 16진수(예: #03A9F4)는 양쪽 모두 인식합니다. 플랫폼 기본값을 사용하려면 비워 두세요."
}
},
"setting_group": {
@@ -1748,7 +1776,7 @@
"all_statuses": "모든 상태",
"ambiguous": "모호함",
"auto_detect": "자동 감지",
"clear": "지우기",
"clear": "명확함",
"completed": "완료됨",
"fine_tuning": "지금 미세 조정 중…",
"force_stopped": "강제 정지됨",
@@ -1910,7 +1938,10 @@
"unlabel_done": "{count}개 사이클의 라벨을 해제했습니다",
"unlabel_failed": "라벨 해제 실패: {error}",
"odometer_saved": "누적 사이클 수가 저장되었습니다",
"odometer_save_failed": "누적 값을 저장할 수 없습니다: {error}"
"odometer_save_failed": "누적 값을 저장할 수 없습니다: {error}",
"contrib_device_changed": "커뮤니티 스토어에 추가했지만 입력 양식이 열려 있는 동안 기기가 변경되었습니다. 설정에서 선택하세요.",
"store_pick_profile_share": "공유할 프로필을 선택하세요",
"remove_failed": "제거 실패: {error}"
},
"suggestion": {
"both_agree": "WashData 권장",
@@ -2010,6 +2041,7 @@
"summary": "잘못 감지된 사이클 {total}개를 제외했습니다: {parts}.",
"reason": {
"force_stopped": "강제 정지",
"user_stopped": "수동으로 정지",
"interrupted": "중단",
"incomplete": "미완료",
"noise": "노이즈",
@@ -60,6 +60,7 @@
"fair_fit_tip": "Vidutinis atitikties nuoseklumas – kai kurie šiam profiliui priskirti ciklai turi žemesnius patikimumo balus. Pažymėkite daugiau ciklų arba iš naujo įrašykite profilį, kad pagerintumėte tikslumą.",
"feedback_requested": "Prašoma atsiliepimo",
"golden_cycle": "Užregistruotas atskaitos ciklas",
"duration_outlier": "{n} neįprastos trukmės ciklų",
"improving": "↗ tobulėjimas",
"manual": "(rankiniu būdu pasirinkta)",
"needs_review": "Reikia peržiūros",
@@ -256,7 +257,15 @@
"hist_scan": "Ieškoti ciklų",
"hist_import_n": "Importuoti ciklus ({n})",
"hist_goto_cycles": "Parodyti ciklus",
"save_total_cycles": "Išsaugoti bendrą skaičių"
"save_total_cycles": "Išsaugoti bendrą skaičių",
"clear": "Išvalyti",
"add_brand": "Pridėti prekės ženklą",
"add_appliance": "Pridėti prietaisą"
},
"note": {
"off_delay": {
"effective": "Pasibaigęs ciklas iš tikrųjų laukia {eff} s, o ne {delay} s: Min. išjungimo tarpas ({gap} s) yra ilgesnis ir turi pirmenybę."
}
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +387,8 @@
"community_store": "Bendruomenės parduotuvė",
"online_account": "Bendruomenės parduotuvė ir interneto funkcijos",
"import_power_history": "Galios istorijos importavimas",
"service_status": "Aptarnavimo būsena"
"service_status": "Aptarnavimo būsena",
"online_account_tab": "Internetas ir bendruomenė"
},
"health": {
"fair": "Priimtina profilio kokybė",
@@ -518,7 +528,8 @@
"pg_tile_matched_tip": "Ciklai, teisingai priskirti žinomai programai",
"pg_time_left": "Likęs laikas",
"pg_weak_match": "Silpna atitiktis",
"phase_name": "Vardas",
"phase_name": "Fazės pavadinimas",
"name": "Vardas",
"phase_timeline": "Fazių laiko juosta",
"power": "Galia",
"profile": "Profilis",
@@ -1027,6 +1038,9 @@
"imported_readonly": "Importuota iš bendruomenės parduotuvės. Rodoma kaip nuoroda ir atitikčiai nustatyti. Ji neįskaičiuojama į jūsų statistiką ir jos negalima redaguoti.",
"merge_no_imports": "Importuotų įrašų negalima sujungti su tikru ciklu. Panaikinkite jų žymėjimą, kad galėtumėte sujungti.",
"store_enable_hint": "Įjunkite interneto funkcijas nustatymuose, kad galėtumėte naršyti ir importuoti bendruomenės etaloninius ciklus.",
"advisory_duration_outlier": "{n} ciklų, pažymėtų '{name}', trukmė per daug skiriasi nuo įprastos šios programos trukmės, kad kada nors ją atitiktų (blogiausias: {ratio}x). Tikėtina, kad jie pažymėti neteisingai arba viename įraše užfiksuoti du paleidimai. Pažymėkite juos iš naujo arba padalykite, kad jie nebeiškraipytų šios programos laiko įverčio.",
"standby_above_stop_title": "Ciklui pasibaigus vis dar naudojama {idle} W",
"standby_above_stop_sub": "{n} iš paskutinių {total} ciklų. Išjungimo delsimas prasideda tik tada, kai galia nukrenta žemiau Sustabdymo slenksčio ({stop} W), todėl ciklai baigiasi pavėluotai arba juos tenka sustabdyti rankiniu būdu.",
"online_intro": "Dalykitės ir atsisiųskite etaloninius ciklus su kitais WashData naudotojais. Visos interneto funkcijos yra pasirenkamos ir pagal numatytuosius nustatymus išjungtos.",
"store_share_intro": "Įkelkite šį etaloninį ciklą, kad juo galėtų naudotis kiti tą patį prietaisą turintys naudotojai. Prieš paskelbiant viešai jis peržiūrimas.",
"online_intro_global": "Naršykite ir bendrinkite etaloninius įrašus su kitais WashData naudotojais bei patvirtinkite prietaisų įrašus. Vienas prisijungimas taikomas visai jūsų WashData integracijai; prietaiso prekės ženklas ir modelis nustatomi kiekvienam prietaisui atskirai skiltyje Pagrindinė konfigūracija. Visos interneto funkcijos yra pasirenkamos ir pagal numatytuosius nustatymus išjungtos.",
@@ -1087,7 +1101,9 @@
"odometer_intro": "Šis bendras skaičius tik didėja. Ciklo įrašo ištrynimas jo nepakeičia, o skaičiavimas tęsiamas ir pasiekus išsaugotos istorijos ribą, todėl aptarnavimo priminimai lieka tikslūs.",
"odometer_correct_hint": "Nustatykite tai, jei WashData užregistravo ciklą, kurio iš tikrųjų nebuvo, arba jei prietaisas jau buvo atlikęs ciklus prieš WashData įdiegimą.",
"no_reminders_set": "Kol kas nenustatyta jokių aptarnavimo priminimų.",
"toast_program_armed": "Programa nustatyta kitam ciklui: {program}"
"toast_program_armed": "Programa nustatyta kitam ciklui: {program}",
"toast_range_required": "Nustatykite tinkamą Nuo/Iki diapazoną.",
"no_profiles_option": "(profilių dar nėra, pridėkite)"
},
"phase_desc": {
"anti_crease": "Po pagrindinio ciklo būgnas intermitiškai sukasi, kad išvengtų audinių raukšlių.",
@@ -1414,8 +1430,8 @@
"label": "Iškrovimo priminimo delsimas"
},
"notify_unload_repeat": {
"doc": "Iškrovimo priminimas pakartotinai siunčiamas kas \"Iškrovimo priminimo delsimas\" minučių, kol neatidarysite durų arba pranešime nepaliesite \"Nustoti priminti\". Reikalingas Durų jutiklio objektas (kaip ir pačiam priminimui). Atmetimo mygtukas veikia Home Assistant mobiliosios (companion) programėlės pranešimuose.",
"label": "Kartoti, kol atsidarys durys"
"doc": "Iškrovimo priminimas pakartotinai siunčiamas kas „Iškrovimo priminimo delsimas“ minučių, kol iškrovimas nebus patvirtintas arba pranešime nepaliesite „Nustoti priminti“. Reikalingas būdas patvirtinti iškrovimą: Durų jutiklio objektas, Iškrovimo patvirtinimo objektas arba žemiau esanti parinktis „Patvirtinti iškrovimą rankiniu būdu“. Atmetimo mygtukas veikia Home Assistant mobiliosios (companion) programėlės pranešimuose.",
"label": "Kartoti, kol bus iškrauta"
},
"off_delay": {
"doc": "Laikas palaukti, kai nukrenta galia, prieš paskelbiant, kad ciklas baigtas. Jei maitinimas atnaujinamas šiame lange, ciklas tęsiasi sklandžiai – tai sujungia pauzes tarp plovimo etapų. Indaplovės turi ilgas džiovinimo fazes (maitinimas išjungiamas 20–60 min.), todėl išjungimo delsa turi būti didesnė už tą, kad visas plovimas+džiovinimas išliktų kaip vienas ciklas.",
@@ -1623,6 +1639,18 @@
"energy_price_dynamic": {
"label": "Pagal laiką svertinė kaina",
"doc": "Kiekvienas ciklas įkainojamas pagal kainą, galiojusią kiekvieną jo veikimo akimirką, o ne pagal vieną kainą, galiojusią jam pasibaigus. Taikoma tik kainos objektui (statinė kaina nekinta). Papildomai nieko nustatyti nereikia: ciklui veikiant kaina sekama tiesiogiai, o skiltyje Diagnostika esantis veiksmas Apdoroti istoriją perskaičiuoja senesnių ciklų kainą pagal recorder duomenis. Išjungta = įprastas veikimas, viena kaina, užfiksuota pabaigoje."
},
"unload_confirm_entity": {
"label": "Iškrovimo patvirtinimo objektas",
"doc": "Pasirenkamas objektas, kurio suveikimas reiškia, kad turinys išimtas; skirtas mašinoms, prie kurių negalima pritaisyti durų jutiklio. Tinka bet koks: Zigbee ar NFC mygtukas, input_button pagalbininkas, judesio jutiklis priešais mašiną, scena. Patvirtinimu laikomas bet koks būsenos pasikeitimas, išskyrus išjungimą ir tapimą nepasiekiamu. Nustačius šį objektą, iškrovimo priminimas įjungiamas ir įrenginiui, kuris neturi Durų jutiklio objekto."
},
"unload_track_without_door": {
"label": "Patvirtinti iškrovimą rankiniu būdu",
"doc": "Naudoti iškrovimo priminimą įrenginyje, kuris neturi nei Durų jutiklio objekto, nei Iškrovimo patvirtinimo objekto, o iškrovimą patvirtinti sava automatika: paspauskite mygtuko objektą „Pažymėti kaip iškrautą“ arba iškvieskite paslaugą ha_washdata.mark_unloaded. Ignoruojama, kai nustatytas Durų jutiklio objektas, nes jis šį signalą jau teikia."
},
"notify_icon_color": {
"label": "Pranešimo spalva",
"doc": "Akcentinė spalva šio prietaiso pranešimams, kad skalbimo mašiną, džiovintuvą ir indaplovę būtų galima atskirti iš pirmo žvilgsnio. iOS nuspalvina piktogramą ir Live Activity eigos juostą užrakto ekrane bei Dynamic Island. Android siunčiama ta pati spalva, tačiau ką su ja daryti, sprendžia pats telefonas: vieni nuspalvina piktogramą, o daugelis (ypač Samsung One UI) ją visiškai ignoruoja. Abi platformos supranta hex kodą (pvz., #03A9F4). Palikite tuščią, kad būtų naudojama numatytoji platformos spalva."
}
},
"setting_group": {
@@ -1716,7 +1744,7 @@
"all_statuses": "Visos būsenos",
"ambiguous": "Dviprasmiška",
"auto_detect": "Automatinis aptikimas",
"clear": "Aišku",
"clear": "Vienareikšmiška",
"completed": "Užbaigta",
"fine_tuning": "patikslinu dabar...",
"force_stopped": "Jėga sustojo",
@@ -1766,6 +1794,7 @@
},
"toast": {
"catalog_refreshed": "Bendruomenės katalogas atnaujintas",
"contrib_device_changed": "Pridėta į bendruomenės parduotuvę, bet, kol forma buvo atidaryta, pasikeitė pasirinktas prietaisas. Pasirinkite jį nustatymuose.",
"access_saved": "Prieigos valdymas išsaugotas",
"all_wiped": "Visi duomenys ištrinti",
"analysis_complete_none": "Analizė baigta: jokių naujų pasiūlymų",
@@ -1878,7 +1907,9 @@
"unlabel_done": "Panaikintas žymėjimas ciklams: {count}",
"unlabel_failed": "Nepavyko panaikinti žymėjimo: {error}",
"odometer_saved": "Bendras ciklų skaičius išsaugotas",
"odometer_save_failed": "Nepavyko išsaugoti bendro skaičiaus: {error}"
"odometer_save_failed": "Nepavyko išsaugoti bendro skaičiaus: {error}",
"store_pick_profile_share": "Pasirinkite profilį, į kurį bendrinti",
"remove_failed": "Nepavyko pašalinti: {error}"
},
"suggestion": {
"both_agree": "WashData rekomenduoja",
@@ -1978,6 +2009,7 @@
"summary": "Neįtraukta {total} klaidingai aptiktų ciklų: {parts}.",
"reason": {
"force_stopped": "priverstinai sustabdyti",
"user_stopped": "rankiniu būdu sustabdyti",
"interrupted": "nutraukti",
"incomplete": "nebaigti",
"noise": "triukšmas",
@@ -2024,7 +2056,7 @@
"duration_weight": "4 etapas: kaip stipriai trukmės atitikimas veikia galutinį balą (numatytoji 0,22)",
"energy_weight": "4 etapas: kaip stipriai energijos atitikimas veikia galutinį balą (numatytoji 0,22)",
"profile_match_min_duration_ratio": "1 etapas: trumpiausias ciklas kaip profilio trukmės dalis; numatytoji 0,1 (10%)",
"profile_match_max_duration_ratio": "1 etapas: ilgiausias ciklas kaip profilio trukmės dalis; numatytoji 1,5 (150%)",
"profile_match_max_duration_ratio": "1 etapas: ilgiausias ciklas kaip profilio trukmės dalis; numatytoji 1,8 (180%)",
"keep_min_score": "2 etapas: žemiausias balas likti lenktynėse; numatytoji 0,1 leidžia silpnus kandidatus, vėlesni etapai parenka geriausią",
"dtw_blend": "3 etapas: 0 = tik pagrindinis balas, 1 = tik DTW balas, 0,5 = lygus mišinys (numatytoji)",
"dtw_ensemble_w": "3 etapas (ansamblio režimas): skalieroto L1 svoris prieš išvestinės DTW; numatytoji 0,7 teikia pirmenybę lygio sąmoningumui",
@@ -60,6 +60,7 @@
"fair_fit_tip": "Mērena atbilstības konsekvence – dažiem šim profilam piešķirtajiem cikliem ir zemāki ticamības rādītāji. Iezīmējiet vairāk ciklu vai atkārtoti ierakstiet profilu, lai uzlabotu precizitāti.",
"feedback_requested": "Pieprasītas atsauksmes",
"golden_cycle": "Reģistrēts atskaites cikls",
"duration_outlier": "{n} neparasta garuma cikli",
"improving": "↗ uzlabošanās",
"manual": "(manuāli atlasīts)",
"needs_review": "Nepieciešama pārskatīšana",
@@ -256,7 +257,15 @@
"hist_scan": "Meklēt ciklus",
"hist_import_n": "Importēt ciklus ({n})",
"hist_goto_cycles": "Parādīt ciklus",
"save_total_cycles": "Saglabāt kopējo skaitu"
"save_total_cycles": "Saglabāt kopējo skaitu",
"clear": "Notīrīt",
"add_brand": "Pievienot zīmolu",
"add_appliance": "Pievienot ierīci"
},
"note": {
"off_delay": {
"effective": "Pabeigts cikls patiesībā gaida {eff} s, nevis {delay} s: Min. izslēgšanas atstarpe ({gap} s) ir garāka, un priekšroka ir tai."
}
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +387,8 @@
"community_store": "Kopienas veikals",
"online_account": "Kopienas veikals un tiešsaistes funkcijas",
"import_power_history": "Jaudas vēstures importēšana",
"service_status": "Apkopes statuss"
"service_status": "Apkopes statuss",
"online_account_tab": "Tiešsaiste un kopiena"
},
"health": {
"fair": "Pieņemama profila kvalitāte",
@@ -518,7 +528,8 @@
"pg_tile_matched_tip": "Cikli, kas pareizi piešķirti zināmai programmai",
"pg_time_left": "Atlikušais laiks",
"pg_weak_match": "Vāja atbilstība",
"phase_name": "Vārds",
"phase_name": "Fāzes nosaukums",
"name": "Vārds",
"phase_timeline": "Fāžu laika josla",
"power": "Jauda",
"profile": "Profils",
@@ -1027,6 +1038,9 @@
"imported_readonly": "Importēts no kopienas veikala. Tiek rādīts atsaucei un saskaņošanai. Tas netiek ieskaitīts jūsu statistikā, un to nevar rediģēt.",
"merge_no_imports": "Importētos ierakstus nevar sapludināt ar īstu ciklu. Noņemiet to atzīmi, lai sapludinātu.",
"store_enable_hint": "Iestatījumos iespējojiet tiešsaistes funkcijas, lai pārlūkotu un importētu kopienas atsauces ciklus.",
"advisory_duration_outlier": "{n} cikliem, kas marķēti kā '{name}', ilgums ir pārāk tālu no tā parastā garuma, lai tam jebkad atbilstu (sliktākais: {ratio}x). Tie, visticamāk, ir nepareizi marķēti vai arī viens ieraksts aptver divas darbības reizes. Pārmarķējiet vai sadaliet tos, lai tie vairs nekropļotu šīs programmas laika aplēsi.",
"standby_above_stop_title": "Ciklam beidzoties, joprojām patērē {idle} W",
"standby_above_stop_sub": "{n} no pēdējiem {total} cikliem. Izslēgšanas aizkave sākas tikai tad, kad jauda ir zem Apstāšanās sliekšņa ({stop} W), tāpēc cikli beidzas ar nokavēšanos vai tie jāaptur manuāli.",
"online_intro": "Kopīgojiet un lejupielādējiet atsauces ciklus ar citiem WashData lietotājiem. Visas tiešsaistes funkcijas ir brīvprātīgas un pēc noklusējuma izslēgtas.",
"store_share_intro": "Augšupielādējiet šo atsauces ciklu, lai to varētu izmantot citi ar to pašu ierīci. Pirms publiskošanas tas tiek pārskatīts.",
"online_intro_global": "Pārlūkojiet un kopīgojiet atsauces ierakstus ar citiem WashData lietotājiem un apstipriniet ierīču ierakstus. Viens savienojums attiecas uz visu jūsu WashData integrāciju; ierīces zīmols un modelis tiek iestatīts katrai ierīcei atsevišķi sadaļā Pamatkonfigurācija. Visas tiešsaistes funkcijas ir brīvprātīgas un pēc noklusējuma izslēgtas.",
@@ -1087,7 +1101,9 @@
"odometer_intro": "Šis kopējais skaits tikai palielinās. Cikla ieraksta dzēšana to nemaina, un skaitīšana turpinās arī pēc saglabātās vēstures ierobežojuma, tāpēc apkopes atgādinājumi paliek precīzi.",
"odometer_correct_hint": "Iestatiet to, ja WashData ir reģistrējis ciklu, kas nekad nav noticis, vai ja ierīce jau bija veikusi ciklus pirms WashData uzstādīšanas.",
"no_reminders_set": "Vēl nav iestatīts neviens apkopes atgādinājums.",
"toast_program_armed": "Programma iestatīta nākamajam ciklam: {program}"
"toast_program_armed": "Programma iestatīta nākamajam ciklam: {program}",
"toast_range_required": "Iestatiet derīgu No/Līdz diapazonu.",
"no_profiles_option": "(vēl nav profilu, pievienojiet vienu)"
},
"phase_desc": {
"anti_crease": "Laiku pa laikam pēc pabeigšanas veiciet īsus gājienus, lai samazinātu grumbiņas.",
@@ -1414,8 +1430,8 @@
"label": "Izkraušanas atgādinājuma aizkave"
},
"notify_unload_repeat": {
"doc": "Atkārtoti nosūta izkraušanas atgādinājumu ik pēc \"Izkraušanas atgādinājuma aizkave\" minūtēm, līdz atverat durvis vai paziņojumā pieskaraties \"Pārtraukt atgādināšanu\". Nepieciešama Durvju sensora entītija (tāpat kā pašam atgādinājumam). Atmešanas poga darbojas Home Assistant mobilās (companion) lietotnes paziņojumos.",
"label": "Atkārtot, līdz atveras durvis"
"doc": "Atkārtoti nosūta izkraušanas atgādinājumu ik pēc „Izkraušanas atgādinājuma aizkave“ minūtēm, līdz izkraušana tiek apstiprināta vai paziņojumā pieskaraties „Pārtraukt atgādināšanu“. Nepieciešams veids, kā apstiprināt izkraušanu: Durvju sensora entītija, Izkraušanas apstiprinājuma entītija vai zemāk esošā opcija „Apstiprināt izkraušanu manuāli“. Atmešanas poga darbojas Home Assistant mobilās (companion) lietotnes paziņojumos.",
"label": "Atkārtot, līdz izkrauts"
},
"off_delay": {
"doc": "Laiks gaidīt, kad jauda samazinās, pirms tiek paziņots, ka cikls ir pabeigts. Ja šajā logā tiek atsākta barošana, cikls turpinās nemanāmi – tas pārslēdz pauzes starp mazgāšanas posmiem. Trauku mazgājamām mašīnām ir garas žāvēšanas fāzes (izslēdziet strāvu uz 20–60 minūtēm), tāpēc izslēgšanas aizkavei ir jābūt lielākai par to, lai visa mazgāšana un žāvēšana būtu viens cikls.",
@@ -1623,6 +1639,18 @@
"energy_price_dynamic": {
"label": "Laikā svērtās izmaksas",
"doc": "Katra cikla izmaksas tiek rēķinātas pēc cenas, kas bija spēkā katrā tā darbības brīdī, nevis pēc vienas cenas, kas bija spēkā tā beigās. Darbojas tikai ar cenas entītiju (statiska cena nemainās). Papildu iestatīšana nav vajadzīga: cikla laikā cena tiek izsekota reāllaikā, bet sadaļā Diagnostika pieejamā funkcija Apstrādāt vēsturi pārrēķina vecāku ciklu izmaksas no recorder datiem. Izslēgts = klasiskā darbība, viena cena, kas fiksēta beigās."
},
"unload_confirm_entity": {
"label": "Izkraušanas apstiprinājuma entītija",
"doc": "Neobligāta entītija, kuras aktivizēšana nozīmē, ka saturs ir izņemts; paredzēta iekārtām, kurām nevar uzstādīt durvju sensoru. Der jebkura: Zigbee vai NFC poga, input_button palīgrīks, kustību sensors iekārtas priekšā, aina. Par apstiprinājumu tiek uzskatītas visas stāvokļa izmaiņas, izņemot izslēgšanu un kļūšanu nepieejamai. Šīs entītijas iestatīšana ieslēdz izkraušanas atgādinājumu arī ierīcei bez Durvju sensora entītijas."
},
"unload_track_without_door": {
"label": "Apstiprināt izkraušanu manuāli",
"doc": "Izmantot izkraušanas atgādinājumu ierīcei, kurai nav ne Durvju sensora entītijas, ne Izkraušanas apstiprinājuma entītijas, un apstiprināt izkraušanu ar savu automatizāciju: nospiediet pogas entītiju „Atzīmēt kā izkrautu“ vai izsauciet pakalpojumu ha_washdata.mark_unloaded. Tiek ignorēts, ja ir iestatīta Durvju sensora entītija, kas šo signālu jau nodrošina."
},
"notify_icon_color": {
"label": "Paziņojuma krāsa",
"doc": "Akcenta krāsa šīs ierīces paziņojumiem, lai veļas mašīnu, žāvētāju un trauku mazgājamo mašīnu varētu atšķirt ar vienu acu uzmetienu. iOS iekrāso ikonu un Live Activity progresa joslu bloķēšanas ekrānā un Dynamic Island. Android tiek nosūtīta tā pati krāsa, taču tas, ko ar to izdara, ir atkarīgs no tālruņa: daži iekrāso ikonu, bet daudzi (jo īpaši Samsung One UI) to pilnībā ignorē. Abas platformas saprot hex kodu (piem., #03A9F4). Atstājiet tukšu, lai izmantotu platformas noklusējuma krāsu."
}
},
"setting_group": {
@@ -1716,7 +1744,7 @@
"all_statuses": "Visi statusi",
"ambiguous": "Neviennozīmīgi",
"auto_detect": "Automātiska noteikšana",
"clear": "Skaidrs",
"clear": "Viennozīmīgi",
"completed": "Pabeigts",
"fine_tuning": "precizēt tagad...",
"force_stopped": "Spēks apstājās",
@@ -1766,6 +1794,7 @@
},
"toast": {
"catalog_refreshed": "Kopienas katalogs atsvaidzināts",
"contrib_device_changed": "Pievienots kopienas veikalā, bet, kamēr veidlapa bija atvērta, mainījās atlasītā ierīce. Atlasiet to sadaļā Iestatījumi.",
"access_saved": "Piekļuves kontrole saglabāta",
"all_wiped": "Visi dati ir izdzēsti",
"analysis_complete_none": "Analīze pabeigta: nav jaunu ieteikumu",
@@ -1878,7 +1907,9 @@
"unlabel_done": "Marķējums noņemts cikliem: {count}",
"unlabel_failed": "Marķējuma noņemšana neizdevās: {error}",
"odometer_saved": "Kopējais ciklu skaits saglabāts",
"odometer_save_failed": "Neizdevās saglabāt kopējo skaitu: {error}"
"odometer_save_failed": "Neizdevās saglabāt kopējo skaitu: {error}",
"store_pick_profile_share": "Izvēlieties profilu, kurā kopīgot",
"remove_failed": "Noņemšana neizdevās: {error}"
},
"suggestion": {
"both_agree": "WashData iesaka",
@@ -1978,6 +2009,7 @@
"summary": "Izslēgti {total} kļūdaini atpazīti cikli: {parts}.",
"reason": {
"force_stopped": "piespiedu kārtā apturēti",
"user_stopped": "manuāli apturēti",
"interrupted": "pārtraukti",
"incomplete": "nepabeigti",
"noise": "troksnis",
@@ -2024,7 +2056,7 @@
"duration_weight": "4. posms: cik stipri ilguma atbilstība ietekmē galīgo rezultātu (noklusējums 0,22)",
"energy_weight": "4. posms: cik stipri enerģijas atbilstība ietekmē galīgo rezultātu (noklusējums 0,22)",
"profile_match_min_duration_ratio": "1. posms: īsākais cikls kā profila ilguma daļa; noklusējums 0,1 (10%)",
"profile_match_max_duration_ratio": "1. posms: garākais cikls kā profila ilguma daļa; noklusējums 1,5 (150%)",
"profile_match_max_duration_ratio": "1. posms: garākais cikls kā profila ilguma daļa; noklusējums 1,8 (180%)",
"keep_min_score": "2. posms: zemākais rezultāts, lai paliktu sacensībā; noklusējums 0,1 pieļauj vājus kandidātus, vēlākie posmi izvēlas labāko",
"dtw_blend": "3. posms: 0 = tikai pamatrezultāts, 1 = tikai DTW rezultāts, 0,5 = vienāds sajaukums (noklusējums)",
"dtw_ensemble_w": "3. posms (ensemble režīms): skalētā L1 svars pret atvasinātās DTW; noklusējums 0,7 dod priekšroku līmeņapziņai",
@@ -60,6 +60,7 @@
"fair_fit_tip": "Умерена конзистентност на совпаѓање - некои циклуси доделени на овој профил имаат пониски оценки за доверба. Обележете повеќе циклуси или повторно снимајте го профилот за да ја подобрите точноста.",
"feedback_requested": "Се бара повратна информација",
"golden_cycle": "Снимен референтен циклус",
"duration_outlier": "Циклуси со необична должина: {n}",
"improving": "↗ подобрување",
"manual": "(рачно избрано)",
"needs_review": "Треба преглед",
@@ -256,7 +257,15 @@
"hist_scan": "Побарај циклуси",
"hist_import_n": "Увези циклуси ({n})",
"hist_goto_cycles": "Прикажи ги циклусите",
"save_total_cycles": "Зачувај го вкупниот број"
"save_total_cycles": "Зачувај го вкупниот број",
"clear": "Исчисти",
"add_brand": "Додај бренд",
"add_appliance": "Додај уред"
},
"note": {
"off_delay": {
"effective": "Завршен циклус всушност чека {eff} с, а не {delay} с: Мин. јаз при исклучување ({gap} с) е подолг и има предност."
}
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +387,8 @@
"community_store": "Продавница на заедницата",
"online_account": "Продавница на заедницата и онлајн функции",
"import_power_history": "Увоз на историја на моќност",
"service_status": "Статус на сервисот"
"service_status": "Статус на сервисот",
"online_account_tab": "Онлајн и заедница"
},
"health": {
"fair": "Прифатлив квалитет на профилот",
@@ -519,7 +529,8 @@
"pg_tile_matched_tip": "Циклуси правилно припишани на позната програма",
"pg_time_left": "Преостанато време",
"pg_weak_match": "Слабо совпаѓање",
"phase_name": "Име",
"phase_name": "Име на фазата",
"name": "Име",
"phase_timeline": "Временска линија на фази",
"power": "Моќ",
"profile": "Профил",
@@ -969,9 +980,9 @@
"toast_error": "Грешка: {error}",
"toast_failed": "Неуспешно: {error}",
"toast_force_stop_failed": "Принудното запирање неуспешно: {error}",
"toast_name_required": "Потребно е ime",
"toast_name_required": "Потребно е име",
"toast_no_automation": "Тука не може да се создаде автоматизација",
"toast_profile_name_required": "Потребно е ime на профил",
"toast_profile_name_required": "Потребно е име на профил",
"toast_program_set": "Поставена програма: {program}",
"toast_rebuild_failed": "Реконструкцијата неуспешна: {error}",
"toast_resume_failed": "Продолжувањето неуспешно: {error}",
@@ -1032,6 +1043,9 @@
"imported_readonly": "Увезено од продавницата на заедницата. Се прикажува за референца и совпаѓање. Не се брои во вашата статистика и не може да се уредува.",
"merge_no_imports": "Увезените снимки не можат да се спојат со вистински циклус. Отштиклирајте ги за да ги споите.",
"store_enable_hint": "Овозможете ги онлајн функциите во поставките за да прелистувате и увезувате референтни циклуси од заедницата.",
"advisory_duration_outlier": "Циклуси означени како '{name}' ({n}) се премногу далеку од нејзината вообичаена должина за да можат некогаш да се совпаднат со неа (најголемо отстапување: {ratio}x). Веројатно се погрешно означени или едно снимање зафатило две извршувања. Означете ги повторно или поделете ги за да престанат да ја искривуваат проценката на времето за оваа програма.",
"standby_above_stop_title": "Сè уште повлекува {idle} Вт кога циклусот заврши",
"standby_above_stop_sub": "{n} од последните {total} циклуси. Одложувањето на исклучување почнува да тече дури кога моќноста ќе падне под Прагот за стоп ({stop} Вт), па циклусите завршуваат доцна или треба да се запрат рачно.",
"online_intro": "Споделувајте и преземајте референтни циклуси со други корисници на WashData. Сите онлајн функции се по избор и стандардно се исклучени.",
"store_share_intro": "Прикачете го овој референтен циклус за да можат да го користат други со истиот уред. Се прегледува пред да стане јавен.",
"online_intro_global": "Прелистувајте и споделувајте референтни снимки со други корисници на WashData и потврдувајте записи за уреди. Едно поврзување важи за целата ваша интеграција WashData; брендот и моделот на уредот се поставуваат за секој уред во разделот „Основно“. Сите онлајн функции се по избор и стандардно се исклучени.",
@@ -1087,7 +1101,9 @@
"odometer_intro": "Овој вкупен број само расте. Бришењето на запис за циклус не го менува и продолжува да брои и над ограничувањето на зачуваната историја, така што потсетниците за сервис остануваат точни.",
"odometer_correct_hint": "Поставете го ова ако WashData сними циклус што никогаш не се случил или ако апаратот веќе извршувал циклуси пред да се инсталира WashData.",
"no_reminders_set": "Сè уште нема поставени потсетници за сервис.",
"toast_program_armed": "Поставена програма за следниот циклус: {program}"
"toast_program_armed": "Поставена програма за следниот циклус: {program}",
"toast_range_required": "Поставете валиден опсег Од/До",
"no_profiles_option": "(нема профили, додајте нов)"
},
"phase_desc": {
"anti_crease": "Повремени кратки превртувања по завршувањето за да се намалат брчките.",
@@ -1414,8 +1430,8 @@
"label": "Задоцнување на потсетникот за истовар"
},
"notify_unload_repeat": {
"doc": "Продолжува да го испраќа потсетникот за истовар на секои \"Задоцнување на потсетникот за истовар\" минути, додека не ја отворите вратата или не притиснете \"Престани да потсетуваш\" во известувањето. Бара Ентитет на сензор за врата (како и самиот потсетник). Копчето за отфрлање работи на известувањата на мобилната апликација на Home Assistant (companion).",
"label": "Повторувај додека не се отвори вратата"
"doc": "Продолжува да го испраќа потсетникот за истовар на секои \"Задоцнување на потсетникот за истовар\" минути, додека истоварот не се потврди или не притиснете \"Престани да потсетуваш\" во известувањето. Потребен е начин за потврда на истоварот: Ентитет на сензор за врата, Ентитет за потврда на истовар или опцијата \"Рачно потврди истовар\" подолу. Копчето за отфрлање работи на известувањата на мобилната апликација на Home Assistant (companion).",
"label": "Повторувај до истовар"
},
"off_delay": {
"doc": "Време е да се чека откако ќе падне напојувањето пред да се прогласи дека циклусот е завршен. Ако напојувањето се обнови во овој прозорец, циклусот продолжува беспрекорно - ова ги премостува паузите помеѓу фазите на перење. Машините за миење садови имаат долги фази на сушење (исклучување на струја 20-60 мин), така што доцнењето на исклучување мора да го надмине тоа за да се задржи целото перење+сушење како еден циклус.",
@@ -1623,6 +1639,18 @@
"energy_price_dynamic": {
"label": "Временски пондериран трошок",
"doc": "Го наплаќа секој циклус по цената што важела во секој момент од неговото работење, наместо по единствената цена што важела кога завршил. Важи само за ентитет на цена (статичната цена не може да се менува). Не бара дополнително поставување: цената се следи во живо додека циклусот трае, а „Обработка на историјата“ во Дијагностика повторно ги пресметува трошоците на постарите циклуси од recorder. Исклучено = класичното однесување, една цена замрзната на крајот."
},
"unload_confirm_entity": {
"label": "Ентитет за потврда на истовар",
"doc": "Изборен ентитет чиешто активирање значи дека содржината е извадена, за машини што не можат да имаат сензор за врата. Работи кој било вид: копче Zigbee или NFC, помошник input_button, сензор за движење пред машината, сцена. Секоја промена на состојбата се смета за потврда, освен исклучување и преминување во недостапна состојба. Поставувањето на ова го овозможува потсетникот за истовар и на уред без Ентитет на сензор за врата."
},
"unload_track_without_door": {
"label": "Рачно потврди истовар",
"doc": "Извршувајте го потсетникот за истовар на уред без Ентитет на сензор за врата и без Ентитет за потврда на истовар, а истоварот потврдете го од сопствена автоматизација: притиснете го ентитетот копче \"Означи како истоварено\" или повикајте ја услугата ha_washdata.mark_unloaded. Се игнорира кога е поставен Ентитет на сензор за врата, кој веќе го дава тој сигнал."
},
"notify_icon_color": {
"label": "Боја на известување",
"doc": "Акцентна боја за известувањата на овој уред, за да се разликуваат машината за перење, машината за сушење и машината за миење садови на прв поглед. iOS ја бои иконата и лентата за напредок на Live Activity на заклучениот екран и во Dynamic Island. Android ја добива истата боја, но што ќе направи со неа зависи од телефонот: некои ја бојат иконата, а многу од нив (особено Samsung One UI) целосно ја игнорираат. И двете платформи разбираат Hex (на пр. #03A9F4). Оставете празно за стандардната боја на платформата."
}
},
"setting_group": {
@@ -1716,7 +1744,7 @@
"all_statuses": "Сите статуси",
"ambiguous": "Двосмислена",
"auto_detect": "Автоматско откривање",
"clear": "Јасно",
"clear": "Недвосмислена",
"completed": "Завршено",
"fine_tuning": "фино подесување сега…",
"force_stopped": "Силата запре",
@@ -1854,6 +1882,7 @@
"store_no_appliance": "Прво поставете го брендот и моделот на уредот во поставките.",
"store_share_failed": "Споделувањето не успеа: {error}",
"store_shared": "Споделено во продавницата на заедницата, чека преглед.",
"contrib_device_changed": "Додадено во продавницата на заедницата, но избраниот уред се смени додека формуларот беше отворен. Изберете го во поставките.",
"appliance_added": "Уредот е додаден, чека одобрување",
"thanks_confirming": "Благодариме за потврдата",
"device_approved": "Одобрено од заедницата",
@@ -1878,7 +1907,9 @@
"unlabel_done": "Отстранета етикета од {count} циклуси",
"unlabel_failed": "Отстранувањето на етикетата не успеа: {error}",
"odometer_saved": "Вкупниот број циклуси е зачуван",
"odometer_save_failed": "Вкупниот број не можеше да се зачува: {error}"
"odometer_save_failed": "Вкупниот број не можеше да се зачува: {error}",
"store_pick_profile_share": "Изберете профил во кој да споделите",
"remove_failed": "Отстранувањето не успеа: {error}"
},
"suggestion": {
"both_agree": "WashData препорачува",
@@ -1978,6 +2009,7 @@
"summary": "Исклучени се {total} погрешно откриени циклуси: {parts}.",
"reason": {
"force_stopped": "присилно запрени",
"user_stopped": "рачно запрени",
"interrupted": "прекинати",
"incomplete": "нецелосни",
"noise": "бучава",
@@ -2024,7 +2056,7 @@
"duration_weight": "Фаза 4: колку силно согласноста на траење влијае на конечниот резултат (стандардно 0,22)",
"energy_weight": "Фаза 4: колку силно согласноста на енергија влијае на конечниот резултат (стандардно 0,22)",
"profile_match_min_duration_ratio": "Фаза 1: минимална должина на циклус како дел од траењето на профилот; стандардно 0,1 (10%)",
"profile_match_max_duration_ratio": "Фаза 1: максимална должина на циклус како дел од траењето на профилот; стандардно 1,5 (150%)",
"profile_match_max_duration_ratio": "Фаза 1: максимална должина на циклус како дел од траењето на профилот; стандардно 1,8 (180%)",
"keep_min_score": "Фаза 2: минимален резултат за останување во трката; стандардно 0,1 прифаќа слаби совпаѓања, подоцнежните фази го избираат најдоброто",
"dtw_blend": "Фаза 3: 0 = само основен резултат, 1 = само DTW резултат, 0,5 = еднакво мешање (стандардно)",
"dtw_ensemble_w": "Фаза 3 (комбиниран режим): тежина на скалиран L1 наспроти изводен DTW; стандардно 0,7 ги фаворизира нивоата",
@@ -60,6 +60,7 @@
"fair_fit_tip": "Moderat matchkonsistens – noen sykluser tilordnet denne profilen har lavere konfidensscore. Merk flere sykluser eller registrer profilen på nytt for å forbedre nøyaktigheten.",
"feedback_requested": "Tilbakemelding forespurt",
"golden_cycle": "Registrert referansesyklus",
"duration_outlier": "{n} syklus(er) med avvikende lengde",
"improving": "↗ forbedres",
"manual": "(valgt manuelt)",
"needs_review": "Trenger gjennomgang",
@@ -256,7 +257,15 @@
"hist_scan": "Søk etter sykluser",
"hist_import_n": "Importer {n} sykluser",
"hist_goto_cycles": "Vis meg syklusene",
"save_total_cycles": "Lagre total"
"save_total_cycles": "Lagre total",
"clear": "Tøm",
"add_brand": "Legg til merke",
"add_appliance": "Legg til apparat"
},
"note": {
"off_delay": {
"effective": "En fullført syklus venter i virkeligheten {eff} s, ikke {delay} s: Min. av-intervall ({gap} s) er lengre og har forrang."
}
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +387,8 @@
"community_store": "Fellesskapsbutikk",
"online_account": "Fellesskapsbutikk og nettfunksjoner",
"import_power_history": "Importer effekthistorikk",
"service_status": "Servicestatus"
"service_status": "Servicestatus",
"online_account_tab": "Nett & fellesskap"
},
"health": {
"fair": "Akseptabel profilkvalitet",
@@ -523,7 +533,8 @@
"pg_tile_matched_tip": "Sykluser som korrekt er tilskrevet et kjent program",
"pg_time_left": "Tid igjen",
"pg_weak_match": "Svakt treff",
"phase_name": "Navn",
"phase_name": "Fasenavn",
"name": "Navn",
"phase_timeline": "Fasetidslinje",
"power": "Effekt",
"profile": "Profil",
@@ -1027,6 +1038,9 @@
"imported_readonly": "Importert fra fellesskapsbutikken. Vises for referanse og matching. Telles ikke med i statistikken din og kan ikke redigeres.",
"merge_no_imports": "Importerte opptak kan ikke slås sammen med en ekte syklus. Fjern merkingen for å slå sammen.",
"store_enable_hint": "Aktiver nettfunksjoner i Innstillinger for å bla gjennom og importere fellesskapets referansesykluser.",
"advisory_duration_outlier": "{n} syklus(er) merket '{name}' ligger for langt fra programmets vanlige lengde til noen gang å kunne matche det (verst: {ratio}x). De er sannsynligvis feilmerket, eller ett opptak fanget to kjøringer. Merk dem på nytt, eller del dem opp, slik at de slutter å forskyve tidsestimatet for dette programmet.",
"standby_above_stop_title": "Trakk fortsatt {idle} W da syklusen ble avsluttet",
"standby_above_stop_sub": "{n} av de siste {total} syklusene. Av-forsinkelsen starter først når effekten er under Stoppterskel ({stop} W), så sykluser avsluttes for sent eller må stoppes for hånd.",
"online_intro": "Del referansesykluser med andre WashData-brukere, og last dem ned. Alle nettfunksjoner er valgfrie og av som standard.",
"store_share_intro": "Last opp denne referansesyklusen slik at andre med samme apparat kan bruke den. Den gjennomgås før den vises offentlig.",
"online_intro_global": "Bla gjennom og del referanseopptak med andre WashData-brukere, og bekreft apparatoppføringer. Én tilkobling gjelder for hele WashData-integrasjonen din; apparatets merke og modell angis per enhet under Grunnleggende. Alle nettfunksjoner er valgfrie og av som standard.",
@@ -1087,7 +1101,9 @@
"odometer_intro": "Denne totalen kan bare gå oppover. Å slette en syklusoppføring endrer den ikke, og den fortsetter å telle utover grensen for lagret historikk, slik at servicepåminnelsene fortsatt stemmer.",
"odometer_correct_hint": "Angi dette hvis WashData har registrert en kjøring som aldri fant sted, eller hvis apparatet allerede hadde kjørt sykluser før WashData ble installert.",
"no_reminders_set": "Ingen servicepåminnelser er satt opp ennå.",
"toast_program_armed": "Program valgt for neste syklus: {program}"
"toast_program_armed": "Program valgt for neste syklus: {program}",
"toast_range_required": "Angi et gyldig Fra/Til-område.",
"no_profiles_option": "(ingen profiler ennå, legg til en)"
},
"pg_desc": {
"dishwasher_end_spike_quiet_release": "Oppvaskmaskin: stille sekunder etter forventet varighet før ventingen på tømming ved slutten av syklusen frigjøres",
@@ -1105,7 +1121,7 @@
"duration_weight": "Trinn 4: hvor sterkt varighetsoverensstemmelse påvirker sluttscoren (standard 0,22)",
"energy_weight": "Trinn 4: hvor sterkt energioverensstemmelse påvirker sluttscoren (standard 0,22)",
"profile_match_min_duration_ratio": "Trinn 1: korteste sykel som andel av profilvarighet; standard 0,1 (10%)",
"profile_match_max_duration_ratio": "Trinn 1: lengste sykel som andel av profilvarighet; standard 1,5 (150%)",
"profile_match_max_duration_ratio": "Trinn 1: lengste sykel som andel av profilvarighet; standard 1,8 (180%)",
"keep_min_score": "Trinn 2: laveste poengsum for å gå videre; standard 0,1 slipper gjennom svake samsvar, senere trinn velger det beste",
"dtw_blend": "Trinn 3: 0 = bare kjernepoengscore, 1 = bare DTW-score, 0,5 = lik blanding (standard)",
"dtw_ensemble_w": "Trinn 3 (ensemble-modus): vekt på skalert L1 kontra derivativ DTW; standard 0,7 foretrekker nivåbevisst",
@@ -1446,8 +1462,8 @@
"label": "Utlastingspåminnelsesforsinkelse"
},
"notify_unload_repeat": {
"doc": "Fortsetter å sende utlastingspåminnelsen på nytt hvert \"Utlastingspåminnelsesforsinkelse\" minutt til du åpner døren eller trykker på \"Stopp påminnelser\" i varselet. Krever en Dørensorenhet (det gjør selve påminnelsen). Avvis-knappen fungerer på varsler fra Home Assistant companion-appen (mobil).",
"label": "Gjenta til døren åpnes"
"doc": "Fortsetter å sende utlastingspåminnelsen på nytt hvert \"Utlastingspåminnelsesforsinkelse\" minutt til utlastingen er bekreftet eller du trykker på \"Stopp påminnelser\" i varselet. Krever en måte å bekrefte utlastingen på: en Dørensorenhet, en Utlastingsbekreftelsesenhet eller alternativet \"Bekreft utlasting manuelt\" nedenfor. Avvis-knappen fungerer på varsler fra Home Assistant companion-appen (mobil).",
"label": "Gjenta til utlastet"
},
"off_delay": {
"doc": "På tide å vente etter at strømmen faller før du erklærer at syklusen er fullført. Hvis strømmen gjenopptas innenfor dette vinduet, fortsetter syklusen sømløst - dette slår bro mellom pauser mellom vasketrinn. Oppvaskmaskiner har lange tørkefaser (strøm av i 20-60 min) så avslåingsforsinkelsen må overstige det for å holde hele vask+tørr som én syklus.",
@@ -1655,6 +1671,18 @@
"energy_price_dynamic": {
"label": "Tidsvektet kostnad",
"doc": "Beregn hver syklus til prisen som gjaldt i hvert øyeblikk den kjørte, i stedet for den ene prisen som gjaldt da den var ferdig. Gjelder bare for en prisenhet (en statisk pris kan ikke endre seg). Krever ikke noe ekstra oppsett: prisen følges live mens syklusen kjører, og \"Behandle historikk\" under Diagnostikk beregner kostnaden for eldre sykluser på nytt fra recorderen. Av = den klassiske oppførselen, én pris frosset ved slutten."
},
"unload_confirm_entity": {
"label": "Utlastingsbekreftelsesenhet",
"doc": "Valgfri enhet der en aktivering betyr at vasken er tatt ut, for maskiner som ikke kan ha en dørsensor. Alle typer fungerer: en Zigbee- eller NFC-knapp, en input_button-hjelper, en bevegelsessensor foran maskinen, en scene. Enhver tilstandsendring teller som en bekreftelse, bortsett fra å bli slått av og å bli utilgjengelig. Å sette denne aktiverer også utlastingspåminnelsen på en enhet uten Dørensorenhet."
},
"unload_track_without_door": {
"label": "Bekreft utlasting manuelt",
"doc": "Kjør utlastingspåminnelsen på en enhet uten Dørensorenhet og uten Utlastingsbekreftelsesenhet, og bekreft utlastingen fra din egen automasjon i stedet: trykk på knappenheten \"Merk som utlastet\", eller kall handlingen ha_washdata.mark_unloaded. Ignoreres når en Dørensorenhet er angitt, siden den allerede gir signalet."
},
"notify_icon_color": {
"label": "Varselfarge",
"doc": "Aksentfarge for varslene fra dette apparatet, slik at en vaskemaskin, en tørketrommel og en oppvaskmaskin er lette å skille fra hverandre. iOS farger ikonet og fremdriftslinjen i Liveaktiviteten på låst skjerm og i Dynamic Island. Android får den samme fargen, men hva den gjør med den, er opp til telefonen: noen farger ikonet, og mange (særlig Samsung One UI) ignorerer den helt. Begge forstår hex-verdier (f.eks. #03A9F4). La stå tomt for plattformens standard."
}
},
"setting_group": {
@@ -1748,7 +1776,7 @@
"all_statuses": "Alle statuser",
"ambiguous": "Tvetydig",
"auto_detect": "Automatisk gjenkjenning",
"clear": "Klar",
"clear": "Entydig",
"completed": "Fullført",
"fine_tuning": "finjustering nå...",
"force_stopped": "Tvangsstoppet",
@@ -1867,6 +1895,7 @@
"summary": "Ekskluderte {total} feildetekterte syklus(er): {parts}.",
"reason": {
"force_stopped": "tvangsstoppet",
"user_stopped": "stoppet manuelt",
"interrupted": "avbrutt",
"incomplete": "ufullstendig",
"noise": "støy",
@@ -2021,7 +2050,10 @@
"unlabel_done": "Merking fjernet fra {count} syklus(er)",
"unlabel_failed": "Fjerning av merking mislyktes: {error}",
"odometer_saved": "Totalt antall sykluser lagret",
"odometer_save_failed": "Kunne ikke lagre totalen: {error}"
"odometer_save_failed": "Kunne ikke lagre totalen: {error}",
"contrib_device_changed": "Lagt til i fellesskapsbutikken, men apparatet ble byttet mens skjemaet var åpent. Velg det i Innstillinger.",
"store_pick_profile_share": "Velg en profil å dele til",
"remove_failed": "Fjerning mislyktes: {error}"
},
"trend": {
"down": "Trender nedover",
@@ -60,6 +60,7 @@
"fair_fit_tip": "Matige matchconsistentie: sommige cycli die aan dit profiel zijn toegewezen, hebben lagere betrouwbaarheidsscores. Label meer cycli of neem het profiel opnieuw op om de nauwkeurigheid te verbeteren.",
"feedback_requested": "Feedback gevraagd",
"golden_cycle": "Geregistreerde referentiecyclus",
"duration_outlier": "{n} cyclus/cycli met afwijkende duur",
"improving": "↗ verbeteren",
"manual": "(handmatig geselecteerd)",
"needs_review": "Moet worden beoordeeld",
@@ -256,7 +257,15 @@
"hist_scan": "Zoeken naar cycli",
"hist_import_n": "{n} cycli importeren",
"hist_goto_cycles": "Toon me de cycli",
"save_total_cycles": "Totaal opslaan"
"save_total_cycles": "Totaal opslaan",
"clear": "Wissen",
"add_brand": "Merk toevoegen",
"add_appliance": "Apparaat toevoegen"
},
"note": {
"off_delay": {
"effective": "Een voltooide cyclus wacht in werkelijkheid {eff} s in plaats van {delay} s: het Min. uitschakelinterval ({gap} s) is langer en heeft voorrang."
}
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +387,8 @@
"community_store": "Communitystore",
"online_account": "Communitystore en onlinefuncties",
"import_power_history": "Vermogensgeschiedenis importeren",
"service_status": "Onderhoudsstatus"
"service_status": "Onderhoudsstatus",
"online_account_tab": "Online & community"
},
"health": {
"fair": "Acceptabele profielkwaliteit",
@@ -523,7 +533,8 @@
"pg_tile_matched_tip": "Cycli die correct aan een bekend programma zijn toegewezen",
"pg_time_left": "Resterende tijd",
"pg_weak_match": "Zwakke match",
"phase_name": "Naam",
"phase_name": "Fasenaam",
"name": "Naam",
"phase_timeline": "Fasetijdlijn",
"power": "Vermogen",
"profile": "Profiel",
@@ -1027,6 +1038,9 @@
"imported_readonly": "Geïmporteerd uit de communitystore. Weergegeven ter referentie en voor matchen. Telt niet mee in je statistieken en kan niet worden bewerkt.",
"merge_no_imports": "Geïmporteerde opnamen kunnen niet worden samengevoegd met een echte cyclus. Deselecteer ze om samen te voegen.",
"store_enable_hint": "Schakel onlinefuncties in bij Instellingen om referentiecycli van de community te bekijken en te importeren.",
"advisory_duration_outlier": "{n} cyclus/cycli met het label '{name}' wijken te ver af van de gebruikelijke duur om er ooit mee te matchen (ergste geval: {ratio}x). Waarschijnlijk zijn ze verkeerd gelabeld, of heeft één opname twee runs vastgelegd. Label ze opnieuw of splits ze, zodat ze de tijdschatting van dit programma niet langer vertekenen.",
"standby_above_stop_title": "Verbruikte nog {idle} W toen de cyclus eindigde",
"standby_above_stop_sub": "{n} van de laatste {total} cycli. De uitschakelvertraging start pas zodra het vermogen onder de Stopdrempel ({stop} W) ligt, waardoor cycli te laat eindigen of met de hand gestopt moeten worden.",
"online_intro": "Deel referentiecycli met andere WashData-gebruikers en download ze. Alle onlinefuncties zijn optioneel en standaard uitgeschakeld.",
"store_share_intro": "Upload deze referentiecyclus zodat anderen met hetzelfde apparaat hem kunnen gebruiken. Hij wordt beoordeeld voordat hij openbaar verschijnt.",
"online_intro_global": "Bekijk en deel referentieopnamen met andere WashData-gebruikers en bevestig apparaatvermeldingen. Eén verbinding geldt voor je hele WashData-integratie; merk en model van het apparaat worden per apparaat ingesteld onder Basis. Alle onlinefuncties zijn optioneel en standaard uitgeschakeld.",
@@ -1087,7 +1101,9 @@
"odometer_intro": "Dit totaal loopt alleen omhoog. Een cyclusregistratie verwijderen verandert het niet, en het blijft doortellen voorbij de limiet van de opgeslagen geschiedenis, zodat serviceherinneringen juist blijven.",
"odometer_correct_hint": "Stel dit in als WashData een draaibeurt heeft geregistreerd die nooit heeft plaatsgevonden, of als het apparaat al cycli had uitgevoerd voordat WashData werd geïnstalleerd.",
"no_reminders_set": "Er zijn nog geen serviceherinneringen ingesteld.",
"toast_program_armed": "Programma klaargezet voor de volgende cyclus: {program}"
"toast_program_armed": "Programma klaargezet voor de volgende cyclus: {program}",
"toast_range_required": "Stel een geldig Van/Tot-bereik in.",
"no_profiles_option": "(nog geen profielen, voeg er een toe)"
},
"pg_desc": {
"dishwasher_end_spike_quiet_release": "Vaatwasser: stille seconden na de verwachte duur voordat de wachttijd op het wegpompen aan het einde van de cyclus wordt vrijgegeven",
@@ -1105,7 +1121,7 @@
"duration_weight": "Stap 4: hoe sterk de duurovereenkomst de eindscore beïnvloedt (standaard 0,22)",
"energy_weight": "Stap 4: hoe sterk de energieovereenkomst de eindscore beïnvloedt (standaard 0,22)",
"profile_match_min_duration_ratio": "Stap 1: minimale cycluslengte als fractie van de profielduur; standaard 0,1 (10%)",
"profile_match_max_duration_ratio": "Stap 1: maximale cycluslengte als fractie van de profielduur; standaard 1,5 (150%)",
"profile_match_max_duration_ratio": "Stap 1: maximale cycluslengte als fractie van de profielduur; standaard 1,8 (180%)",
"keep_min_score": "Stap 2: minimale score om in de race te blijven; standaard 0,1 laat zwakke kandidaten toe, latere stappen kiezen de beste",
"dtw_blend": "Stap 3: 0 = alleen kernscore, 1 = alleen DTW-score, 0,5 = gelijke mix (standaard)",
"dtw_ensemble_w": "Stap 3 (ensemble-modus): gewicht op geschaalde L1 vs afgeleid DTW; standaard 0,7 geeft voorkeur aan niveauafhankelijke herkenning",
@@ -1446,8 +1462,8 @@
"label": "Vertraging uitlaadherinnering"
},
"notify_unload_repeat": {
"doc": "Blijft de uitlaadherinnering elke \"Vertraging uitlaadherinnering\" minuten opnieuw verzenden totdat u de deur opent of in de melding op \"Stop met herinneren\" tikt. Vereist een Deursensorentiteit (de herinnering zelf ook). De sluitknop werkt bij meldingen van de Home Assistant companion-app (mobiel).",
"label": "Herhalen tot de deur opengaat"
"doc": "Blijft de uitlaadherinnering elke \"Vertraging uitlaadherinnering\" minuten opnieuw verzenden totdat het uitladen is bevestigd of u in de melding op \"Stop met herinneren\" tikt. Vereist een manier om het uitladen te bevestigen: een Deursensorentiteit, een Uitlaadbevestigingsentiteit of de optie \"Uitladen handmatig bevestigen\" hieronder. De sluitknop werkt bij meldingen van de Home Assistant companion-app (mobiel).",
"label": "Herhalen tot uitgeladen"
},
"off_delay": {
"doc": "Tijd om te wachten nadat de stroom is uitgevallen voordat de cyclus als voltooid wordt verklaard. Als de stroom binnen dit venster wordt hervat, gaat de cyclus naadloos verder - dit overbrugt pauzes tussen wasfasen. Vaatwassers hebben lange droogfasen (uitschakeling gedurende 20-60 minuten), dus de uitschakelvertraging moet groter zijn dan die om de hele was+droogcyclus als één cyclus te laten verlopen.",
@@ -1655,6 +1671,18 @@
"energy_price_dynamic": {
"label": "Tijdgewogen kosten",
"doc": "Reken elke cyclus af tegen de prijs die op elk moment van de cyclus gold, in plaats van tegen de ene prijs die gold toen hij klaar was. Geldt alleen voor een prijsentiteit (een statische prijs kan niet bewegen). Vraagt geen extra instellingen: tijdens de cyclus wordt de prijs live bijgehouden, en \"Geschiedenis verwerken\" onder Diagnostiek berekent de kosten van oudere cycli opnieuw uit de recorder. Uit = het klassieke gedrag, één prijs bevroren aan het eind."
},
"unload_confirm_entity": {
"label": "Uitlaadbevestigingsentiteit",
"doc": "Optionele entiteit waarvan de activering betekent dat de lading eruit is gehaald, voor machines die geen deursensor kunnen hebben. Elk type werkt: een Zigbee- of NFC-knop, een input_button-helper, een bewegingssensor voor de machine, een scène. Elke statuswijziging telt als bevestiging, behalve uitschakelen en niet beschikbaar worden. Door dit in te stellen wordt de uitlaadherinnering ook ingeschakeld op een apparaat zonder Deursensorentiteit."
},
"unload_track_without_door": {
"label": "Uitladen handmatig bevestigen",
"doc": "Voert de uitlaadherinnering uit op een apparaat zonder Deursensorentiteit en zonder Uitlaadbevestigingsentiteit, waarbij u het uitladen in plaats daarvan vanuit uw eigen automatisering bevestigt: druk op de knopentiteit \"Markeren als uitgeladen\" of roep de actie ha_washdata.mark_unloaded aan. Wordt genegeerd wanneer er een Deursensorentiteit is ingesteld, die het signaal al levert."
},
"notify_icon_color": {
"label": "Meldingskleur",
"doc": "Accentkleur voor de meldingen van dit apparaat, zodat een wasmachine, een droger en een vaatwasser in één oogopslag te onderscheiden zijn. iOS kleurt het pictogram en de voortgangsbalk van de Live-activiteit op het toegangsscherm en in de Dynamic Island. Android krijgt dezelfde kleur doorgestuurd, maar wat ermee gebeurt, bepaalt de telefoon: sommige kleuren het pictogram en veel toestellen (zeker Samsung One UI) negeren de kleur volledig. Beide begrijpen hex-waarden (bijvoorbeeld #03A9F4). Laat dit leeg voor de platformstandaard."
}
},
"setting_group": {
@@ -1748,7 +1776,7 @@
"all_statuses": "Alle statussen",
"ambiguous": "Ambigu",
"auto_detect": "Automatische detectie",
"clear": "Duidelijk",
"clear": "Eenduidig",
"completed": "Voltooid",
"fine_tuning": "nu aan het finetunen...",
"force_stopped": "Geforceerd gestopt",
@@ -1867,6 +1895,7 @@
"summary": "{total} verkeerd gedetecteerde cyclus(sen) uitgesloten: {parts}.",
"reason": {
"force_stopped": "geforceerd gestopt",
"user_stopped": "handmatig gestopt",
"interrupted": "onderbroken",
"incomplete": "onvolledig",
"noise": "ruis",
@@ -2021,7 +2050,10 @@
"unlabel_done": "Label verwijderd van {count} cyclus(sen)",
"unlabel_failed": "Label verwijderen mislukt: {error}",
"odometer_saved": "Totaal aantal cycli opgeslagen",
"odometer_save_failed": "Kan totaal niet opslaan: {error}"
"odometer_save_failed": "Kan totaal niet opslaan: {error}",
"contrib_device_changed": "Toegevoegd aan de communitystore, maar er is van apparaat gewisseld terwijl het formulier openstond. Selecteer het bij Instellingen.",
"store_pick_profile_share": "Kies een profiel om naar te delen",
"remove_failed": "Verwijderen mislukt: {error}"
},
"trend": {
"down": "Trend naar beneden",
@@ -60,6 +60,7 @@
"fair_fit_tip": "Umiarkowana spójność dopasowania – niektóre cykle przypisane do tego profilu mają niższy poziom pewności. Oznacz więcej cykli lub ponownie zapisz profil, aby poprawić dokładność.",
"feedback_requested": "Poproszono o opinię",
"golden_cycle": "Zarejestrowany cykl referencyjny",
"duration_outlier": "Cykle o nietypowej długości: {n}",
"improving": "↗ poprawa",
"manual": "(wybierane ręcznie)",
"needs_review": "Wymaga recenzji",
@@ -256,7 +257,15 @@
"hist_scan": "Szukaj cykli",
"hist_import_n": "Importuj cykle: {n}",
"hist_goto_cycles": "Pokaż cykle",
"save_total_cycles": "Zapisz łączną liczbę"
"save_total_cycles": "Zapisz łączną liczbę",
"clear": "Wyczyść",
"add_brand": "Dodaj markę",
"add_appliance": "Dodaj urządzenie"
},
"note": {
"off_delay": {
"effective": "Zakończony cykl faktycznie czeka {eff} s, a nie {delay} s: Minimalna Przerwa Wyłączenia ({gap} s) jest dłuższa i ma pierwszeństwo."
}
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +387,8 @@
"community_store": "Sklep społecznościowy",
"online_account": "Sklep społecznościowy i funkcje online",
"import_power_history": "Import historii mocy",
"service_status": "Status serwisu"
"service_status": "Status serwisu",
"online_account_tab": "Online i społeczność"
},
"health": {
"fair": "Akceptowalna jakość profilu",
@@ -519,7 +529,8 @@
"pg_tile_matched_tip": "Cykle poprawnie przypisane do znanego programu",
"pg_time_left": "Pozostały czas",
"pg_weak_match": "Słabe dopasowanie",
"phase_name": "Nazwa",
"phase_name": "Nazwa fazy",
"name": "Nazwa",
"phase_timeline": "Oś czasu faz",
"power": "Moc",
"profile": "Profil",
@@ -1032,6 +1043,9 @@
"imported_readonly": "Zaimportowano ze sklepu społecznościowego. Wyświetlane w celach referencyjnych i do dopasowywania. Nie liczy się do Twoich statystyk i nie można go edytować.",
"merge_no_imports": "Zaimportowanych nagrań nie można scalić z rzeczywistym cyklem. Odznacz je, aby scalić.",
"store_enable_hint": "Włącz funkcje online w Ustawieniach, aby przeglądać i importować cykle referencyjne społeczności.",
"advisory_duration_outlier": "Cykle oznaczone jako '{name}' ({n}) są zbyt odległe od jego zwykłej długości, aby kiedykolwiek mogły zostać z nim dopasowane (największe odchylenie: {ratio}x). Prawdopodobnie są błędnie oznaczone albo jedno nagranie objęło dwa uruchomienia. Oznacz je ponownie lub podziel, aby przestały zniekształcać szacowany czas tego programu.",
"standby_above_stop_title": "Nadal pobiera {idle} W w momencie zakończenia cyklu",
"standby_above_stop_sub": "{n} z ostatnich {total} cykli. Opóźnienie Wyłączenia zaczyna biec dopiero wtedy, gdy moc spadnie poniżej Progu Zatrzymania ({stop} W), więc cykle kończą się z opóźnieniem albo trzeba je zatrzymywać ręcznie.",
"online_intro": "Udostępniaj i pobieraj cykle referencyjne z innymi użytkownikami WashData. Wszystkie funkcje online są dobrowolne i domyślnie wyłączone.",
"store_share_intro": "Prześlij ten cykl referencyjny, aby inni z tym samym urządzeniem mogli go używać. Zostanie sprawdzony przed publicznym udostępnieniem.",
"online_intro_global": "Przeglądaj i udostępniaj nagrania referencyjne innym użytkownikom WashData oraz potwierdzaj wpisy urządzeń. Jedno połączenie dotyczy całej integracji WashData; markę i model urządzenia ustawia się osobno dla każdego urządzenia w sekcji Podstawowe. Wszystkie funkcje online są dobrowolne i domyślnie wyłączone.",
@@ -1087,7 +1101,9 @@
"odometer_intro": "Ta łączna liczba tylko rośnie. Usunięcie zapisu cyklu jej nie zmienia, a licznik działa dalej także po przekroczeniu limitu przechowywanej historii, więc przypomnienia serwisowe pozostają prawidłowe.",
"odometer_correct_hint": "Ustaw tę wartość, jeśli WashData zapisał cykl, który nigdy się nie odbył, albo jeśli urządzenie wykonało już cykle przed instalacją WashData.",
"no_reminders_set": "Nie ustawiono jeszcze żadnych przypomnień serwisowych.",
"toast_program_armed": "Program ustawiony na następny cykl: {program}"
"toast_program_armed": "Program ustawiony na następny cykl: {program}",
"toast_range_required": "Ustaw prawidłowy zakres Od/Do",
"no_profiles_option": "(brak profili, dodaj nowy)"
},
"phase_desc": {
"anti_crease": "Sporadyczne krótkie obroty bębna po zakończeniu w celu zapobiegania gnieceniu tkanin.",
@@ -1410,8 +1426,8 @@
"label": "Opóźnienie Przypomnienia o Rozładunku"
},
"notify_unload_repeat": {
"doc": "Ponownie wysyła przypomnienie o rozładunku co \"Opóźnienie Przypomnienia o Rozładunku\" minut, dopóki nie otworzysz drzwi lub nie dotkniesz \"Przestań przypominać\" w powiadomieniu. Wymaga Encji Czujnika Drzwi (tak jak samo przypomnienie). Przycisk odrzucenia działa w powiadomieniach aplikacji mobilnej Home Assistant (companion app).",
"label": "Powtarzaj, aż otworzą się drzwi"
"doc": "Ponownie wysyła przypomnienie o rozładunku co „Opóźnienie Przypomnienia o Rozładunku” minut, dopóki rozładunek nie zostanie potwierdzony lub nie dotkniesz „Przestań przypominać” w powiadomieniu. Wymaga sposobu potwierdzenia rozładunku: Encji Czujnika Drzwi, Encji Potwierdzenia Rozładunku albo opcji „Ręczne potwierdzanie rozładunku” poniżej. Przycisk odrzucenia działa w powiadomieniach aplikacji mobilnej Home Assistant (companion app).",
"label": "Powtarzaj do rozładowania"
},
"off_delay": {
"doc": "Czas oczekiwania po spadku mocy przed ogłoszeniem zakończenia cyklu. Jeżeli w tym oknie zasilanie zostanie przywrócone, cykl będzie przebiegał płynnie – eliminując przerwy pomiędzy etapami prania. Zmywarki mają długie fazy suszenia (wyłączenie zasilania na 20–60 minut), więc opóźnienie wyłączenia musi być dłuższe, aby całe pranie+suszenie trwało w jednym cyklu.",
@@ -1623,6 +1639,18 @@
"curve_preroll_seconds": {
"doc": "Ile sekund odczytów z przerwanych prób startu WashData może dołączyć na początku krzywej cyklu. Krzywa cyklu zwykle zaczyna się od tej próby startu, która ostatecznie się powiodła, więc w maszynie, która próbuje kilka razy, zanim się rozkręci - wybór programu, blokada drzwi, pierwsze napełnianie - w każdej krzywej brakuje pierwszych minut rzeczywistej pracy. Ustaw tę wartość nieco powyżej najdłuższego takiego rozbiegu Twojej maszyny (300 s w zupełności wystarczy w większości przypadków). 0 wyłącza tę funkcję. Pamiętaj, że jej włączenie przesuwa zapisany początek wcześniej, więc cykle zapisane przed zmianą i po niej mają różne czasy trwania dla tego samego programu; wyuczone średnie będą się przesuwać, dopóki starsze cykle nie wypadną z historii.",
"label": "Wstępny zapis krzywej"
},
"unload_confirm_entity": {
"label": "Encja Potwierdzenia Rozładunku",
"doc": "Opcjonalna encja, której aktywacja oznacza, że wsad został wyjęty, dla maszyn, w których nie da się zamontować czujnika drzwi. Działa dowolny rodzaj: przycisk Zigbee lub NFC, pomocnik input_button, czujnik ruchu przed maszyną, scena. Każda zmiana stanu liczy się jako potwierdzenie, z wyjątkiem wyłączenia i przejścia w stan niedostępny. Ustawienie tej opcji włącza też przypomnienie o rozładunku na urządzeniu bez Encji Czujnika Drzwi."
},
"unload_track_without_door": {
"label": "Ręczne potwierdzanie rozładunku",
"doc": "Uruchamia przypomnienie o rozładunku na urządzeniu bez Encji Czujnika Drzwi i bez Encji Potwierdzenia Rozładunku, a rozładunek potwierdzasz z własnej automatyzacji: naciśnij encję przycisku „Oznacz jako rozładowane” lub wywołaj usługę ha_washdata.mark_unloaded. Ignorowane, gdy ustawiona jest Encja Czujnika Drzwi, która już dostarcza ten sygnał."
},
"notify_icon_color": {
"label": "Kolor Powiadomienia",
"doc": "Kolor akcentu powiadomień tego urządzenia, aby pralkę, suszarkę i zmywarkę można było odróżnić na pierwszy rzut oka. iOS koloruje ikonę oraz pasek postępu Live Activity na ekranie blokady i w Dynamic Island. Android otrzymuje ten sam kolor, ale to telefon decyduje, co z nim zrobi: niektóre kolorują ikonę, a wiele (zwłaszcza Samsung One UI) całkowicie go ignoruje. Obie platformy rozumieją Hex (np. #03A9F4). Pozostaw puste, aby użyć domyślnego koloru platformy."
}
},
"setting_group": {
@@ -1716,7 +1744,7 @@
"all_statuses": "Wszystkie statusy",
"ambiguous": "Dwuznaczny",
"auto_detect": "Automatyczne wykrywanie",
"clear": "Jasne",
"clear": "Jednoznaczny",
"completed": "Zakończony",
"fine_tuning": "teraz dostrajanie…",
"force_stopped": "Wymuszone zatrzymanie",
@@ -1835,6 +1863,7 @@
"summary": "Wykluczono {total} błędnie wykrytych cykli: {parts}.",
"reason": {
"force_stopped": "zatrzymane przymusowo",
"user_stopped": "zatrzymane ręcznie",
"interrupted": "przerwane",
"incomplete": "niekompletne",
"noise": "hałas",
@@ -1965,6 +1994,7 @@
"store_no_appliance": "Najpierw ustaw markę i model urządzenia w Ustawieniach.",
"store_share_failed": "Udostępnianie nie powiodło się: {error}",
"store_shared": "Udostępniono w sklepie społecznościowym, oczekuje na weryfikację.",
"contrib_device_changed": "Dodano do sklepu społecznościowego, ale wybrane urządzenie zmieniło się, gdy formularz był otwarty. Wybierz je w Ustawieniach.",
"appliance_added": "Urządzenie dodane, oczekuje na zatwierdzenie",
"thanks_confirming": "Dziękujemy za potwierdzenie",
"device_approved": "Zatwierdzone przez społeczność",
@@ -1989,7 +2019,9 @@
"unlabel_done": "Usunięto etykietę z {count} cykli",
"unlabel_failed": "Usunięcie etykiety nie powiodło się: {error}",
"odometer_saved": "Łączna liczba cykli zapisana",
"odometer_save_failed": "Nie udało się zapisać łącznej liczby: {error}"
"odometer_save_failed": "Nie udało się zapisać łącznej liczby: {error}",
"store_pick_profile_share": "Wybierz profil do udostępnienia",
"remove_failed": "Usuwanie nie powiodło się: {error}"
},
"trend": {
"down": "Trend w dół",
@@ -2024,7 +2056,7 @@
"duration_weight": "Etap 4: jak mocno zgodność czasu trwania wpływa na wynik końcowy (domyślnie 0,22)",
"energy_weight": "Etap 4: jak mocno zgodność energii wpływa na wynik końcowy (domyślnie 0,22)",
"profile_match_min_duration_ratio": "Etap 1: minimalna długość cyklu jako ułamek czasu trwania profilu; domyślnie 0,1 (10%)",
"profile_match_max_duration_ratio": "Etap 1: maksymalna długość cyklu jako ułamek czasu trwania profilu; domyślnie 1,5 (150%)",
"profile_match_max_duration_ratio": "Etap 1: maksymalna długość cyklu jako ułamek czasu trwania profilu; domyślnie 1,8 (180%)",
"keep_min_score": "Etap 2: minimalny wynik, by pozostać w wyścigu; domyślnie 0,1 dopuszcza słabe dopasowania, późniejsze etapy wybierają najlepsze",
"dtw_blend": "Etap 3: 0 = tylko wynik główny, 1 = tylko wynik DTW, 0,5 = równe mieszanie (domyślnie)",
"dtw_ensemble_w": "Etap 3 (tryb kombinacji): waga skalowanego L1 względem pochodnego DTW; domyślnie 0,7 faworyzuje poziom",
@@ -35,7 +35,8 @@
"not_importable": "n/d aqui",
"exists": "já existe",
"backfilled_tip": "Detectado em um histórico de potência importado. Influencia apenas a correspondência de programas, não é contabilizado nas estatísticas.",
"armed": "(aplica-se ao próximo ciclo)"
"armed": "(aplica-se ao próximo ciclo)",
"duration_outlier": "{n} ciclo(s) com duração incomum"
},
"btn": {
"reset_zoom": "Redefinir zoom",
@@ -208,7 +209,10 @@
"hist_scan": "Procurar ciclos",
"hist_import_n": "Importar {n} ciclos",
"hist_goto_cycles": "Ver os ciclos",
"save_total_cycles": "Salvar total"
"save_total_cycles": "Salvar total",
"clear": "Limpar",
"add_brand": "Adicionar marca",
"add_appliance": "Adicionar eletrodoméstico"
},
"conflict": {
"anti_wrinkle_exit": {
@@ -330,7 +334,8 @@
"community_store": "Loja da comunidade",
"online_account": "Loja da comunidade e recursos online",
"import_power_history": "Importar histórico de potência",
"service_status": "Status da manutenção"
"service_status": "Status da manutenção",
"online_account_tab": "Online e comunidade"
},
"health": {
"fair": "Qualidade de perfil aceitável",
@@ -470,7 +475,8 @@
"pg_tile_matched_tip": "Ciclos atribuídos corretamente a um programa conhecido",
"pg_time_left": "Tempo restante",
"pg_weak_match": "Correspondência fraca",
"phase_name": "Nome",
"phase_name": "Nome da fase",
"name": "Nome",
"phase_timeline": "Linha do tempo de fases",
"power": "Potência",
"profile": "Perfil",
@@ -1039,7 +1045,12 @@
"odometer_intro": "Este total só aumenta. Excluir o registro de um ciclo não o altera e ele continua contando além do limite do histórico armazenado, para que os lembretes de manutenção permaneçam corretos.",
"odometer_correct_hint": "Defina este valor se o WashData registrou um ciclo que nunca aconteceu, ou se o eletrodoméstico já havia realizado ciclos antes de o WashData ser instalado.",
"no_reminders_set": "Ainda não há lembretes de manutenção definidos.",
"toast_program_armed": "Programa preparado para o próximo ciclo: {program}"
"toast_program_armed": "Programa preparado para o próximo ciclo: {program}",
"standby_above_stop_title": "Ainda consumindo {idle} W quando o ciclo terminou",
"standby_above_stop_sub": "{n} dos últimos {total} ciclos. O Atraso de Desligamento só começa quando a potência fica abaixo do Limite de Parada ({stop} W), então os ciclos terminam tarde ou precisam ser parados na mão.",
"advisory_duration_outlier": "{n} ciclo(s) rotulados como '{name}' estão longe demais da duração habitual dele para algum dia corresponderem (pior caso: {ratio}x). Provavelmente estão rotulados errado, ou uma gravação capturou duas execuções. Rotule-os novamente ou divida-os para que parem de distorcer a estimativa de tempo deste programa.",
"toast_range_required": "Defina um intervalo De/Até válido",
"no_profiles_option": "(sem perfis, adicione um)"
},
"phase_desc": {
"anti_crease": "Giros curtos ocasionais após a conclusão para reduzir amassados.",
@@ -1366,8 +1377,8 @@
"label": "Atraso de Lembrete de Descarga"
},
"notify_unload_repeat": {
"doc": "Continua reenviando o lembrete de descarga a cada \"Atraso de Lembrete de Descarga\" minutos até você abrir a porta ou tocar em \"Parar de lembrar\" na notificação. Requer uma Entidade do Sensor de Porta (o próprio lembrete a exige). O botão de dispensar funciona nas notificações do aplicativo complementar (móvel) do Home Assistant.",
"label": "Repetir Até a Porta Abrir"
"doc": "Continua reenviando o lembrete de descarga a cada \"Atraso de Lembrete de Descarga\" minutos até a descarga ser confirmada ou você tocar em \"Parar de lembrar\" na notificação. Requer uma forma de confirmar a descarga: uma Entidade do Sensor de Porta, uma Entidade de Confirmação de Descarga ou a opção \"Confirmar a Descarga Manualmente\" abaixo. O botão de dispensar funciona nas notificações do aplicativo complementar (móvel) do Home Assistant.",
"label": "Repetir Até Confirmar a Descarga"
},
"off_delay": {
"doc": "Tempo de espera após a queda de energia antes de declarar o término do ciclo. Se a energia for retomada dentro desta janela, o ciclo continua perfeitamente - isso faz a ponte entre as etapas de lavagem. As máquinas de lavar louça têm longas fases de secagem (desligadas por 20-60 minutos), portanto o atraso no desligamento deve exceder esse valor para manter toda a lavagem + secagem em um único ciclo.",
@@ -1575,6 +1586,18 @@
"energy_price_dynamic": {
"label": "Custo Ponderado no Tempo",
"doc": "Cobra cada ciclo pelo preço em vigor em cada momento em que ele rodou, em vez do preço único do instante em que terminou. Só vale para uma entidade de preço (um preço estático não muda). Não exige configuração extra: o preço é acompanhado ao vivo enquanto o ciclo roda, e Processar histórico, em Diagnósticos, recalcula o custo dos ciclos antigos a partir do recorder. Desativado = o comportamento clássico, um preço congelado no final."
},
"unload_confirm_entity": {
"label": "Entidade de Confirmação de Descarga",
"doc": "Entidade opcional cuja ativação significa que a carga foi retirada, para máquinas que não podem ter um sensor de porta. Serve qualquer tipo: um botão Zigbee ou NFC, um auxiliar input_button, um sensor de movimento na frente da máquina, uma cena. Qualquer mudança de estado conta como confirmação, exceto desligar e ficar indisponível. Definir isso também ativa o lembrete de descarga em um dispositivo sem Entidade do Sensor de Porta."
},
"unload_track_without_door": {
"label": "Confirmar a Descarga Manualmente",
"doc": "Executa o lembrete de descarga em um dispositivo sem Entidade do Sensor de Porta e sem Entidade de Confirmação de Descarga, e você confirma a descarga pela sua própria automação: pressione a entidade de botão \"Marcar como descarregado\" ou chame o serviço ha_washdata.mark_unloaded. É ignorado quando uma Entidade do Sensor de Porta está definida, pois ela já fornece o sinal."
},
"notify_icon_color": {
"label": "Cor da Notificação",
"doc": "Cor de destaque para as notificações deste eletrodoméstico, para distinguir de relance uma lavadora, uma secadora e uma lava-louças. O iOS colore o ícone e a barra de progresso da Atividade ao Vivo na Tela Bloqueada e na Dynamic Island. O Android recebe a mesma cor, mas o que ele faz com ela depende do celular: alguns colorem o ícone e muitos (em especial o Samsung One UI) a ignoram por completo. Ambos entendem o formato hexadecimal (por exemplo, #03A9F4). Deixe em branco para o padrão da plataforma."
}
},
"setting_group": {
@@ -1668,7 +1691,7 @@
"all_statuses": "Todos os status",
"ambiguous": "Ambíguo",
"auto_detect": "Detectar automaticamente",
"clear": "Limpar",
"clear": "Inequívoco",
"completed": "Concluído",
"fine_tuning": "ajustando…",
"force_stopped": "Parado forçadamente",
@@ -1807,6 +1830,7 @@
"store_share_failed": "Falha no compartilhamento: {error}",
"store_shared": "Compartilhado na loja da comunidade, aguardando revisão.",
"appliance_added": "Eletrodoméstico adicionado, aguardando aprovação",
"contrib_device_changed": "Adicionado à loja da comunidade, mas o dispositivo selecionado mudou enquanto o formulário estava aberto. Selecione-o em Configurações.",
"thanks_confirming": "Obrigado por confirmar",
"device_approved": "Aprovado pela comunidade",
"rating_saved": "Avaliação de qualidade salva",
@@ -1830,7 +1854,9 @@
"unlabel_done": "Rótulo removido de {count} ciclo(s)",
"unlabel_failed": "Falha ao remover o rótulo: {error}",
"odometer_saved": "Total de ciclos salvo",
"odometer_save_failed": "Não foi possível salvar o total: {error}"
"odometer_save_failed": "Não foi possível salvar o total: {error}",
"store_pick_profile_share": "Escolha um perfil no qual compartilhar",
"remove_failed": "Falha ao remover: {error}"
},
"suggestion": {
"both_agree": "WashData recomenda",
@@ -1930,6 +1956,7 @@
"summary": "Excluído(s) {total} ciclo(s) mal detectado(s): {parts}.",
"reason": {
"force_stopped": "parados forçadamente",
"user_stopped": "parados manualmente",
"interrupted": "interrompidos",
"incomplete": "incompletos",
"noise": "ruído",
@@ -1976,7 +2003,7 @@
"duration_weight": "Etapa 4: o quanto a concordância de duração afeta a pontuação final (padrão 0,22)",
"energy_weight": "Etapa 4: o quanto a concordância de energia afeta a pontuação final (padrão 0,22)",
"profile_match_min_duration_ratio": "Etapa 1: duração mínima do ciclo como fração da duração do perfil; padrão 0,1 (10%)",
"profile_match_max_duration_ratio": "Etapa 1: duração máxima do ciclo como fração da duração do perfil; padrão 1,5 (150%)",
"profile_match_max_duration_ratio": "Etapa 1: duração máxima do ciclo como fração da duração do perfil; padrão 1,8 (180%)",
"keep_min_score": "Etapa 2: pontuação mínima para permanecer na disputa; o padrão 0,1 admite candidatos fracos, etapas posteriores escolhem o melhor",
"dtw_blend": "Etapa 3: 0 = apenas pontuação central, 1 = apenas pontuação DTW, 0,5 = mistura equilibrada (padrão)",
"dtw_ensemble_w": "Etapa 3 (modo ensemble): peso em L1 escalado vs DTW derivativo; o padrão 0,7 favorece a sensibilidade ao nível",
@@ -2187,5 +2214,10 @@
"hold_action": "Ação ao manter pressionado",
"double_tap_action": "Ação ao tocar duas vezes"
}
},
"note": {
"off_delay": {
"effective": "Um ciclo concluído espera na verdade {eff} s, e não {delay} s: o Intervalo Mínimo de Desligamento ({gap} s) é maior e tem prioridade."
}
}
}
@@ -35,7 +35,8 @@
"not_importable": "n/d aqui",
"exists": "já existe",
"backfilled_tip": "Detetado num histórico de potência importado. Influencia apenas a correspondência de programas, não é contabilizado nas estatísticas.",
"armed": "(aplica-se ao próximo ciclo)"
"armed": "(aplica-se ao próximo ciclo)",
"duration_outlier": "{n} ciclo(s) com duração invulgar"
},
"btn": {
"reset_zoom": "Repor zoom",
@@ -208,7 +209,10 @@
"hist_scan": "Procurar ciclos",
"hist_import_n": "Importar {n} ciclos",
"hist_goto_cycles": "Ver os ciclos",
"save_total_cycles": "Guardar total"
"save_total_cycles": "Guardar total",
"clear": "Limpar",
"add_brand": "Adicionar marca",
"add_appliance": "Adicionar electrodoméstico"
},
"conflict": {
"anti_wrinkle_exit": {
@@ -330,7 +334,8 @@
"community_store": "Loja da comunidade",
"online_account": "Loja da comunidade e funcionalidades online",
"import_power_history": "Importar histórico de potência",
"service_status": "Estado da manutenção"
"service_status": "Estado da manutenção",
"online_account_tab": "Online e comunidade"
},
"health": {
"fair": "Qualidade de perfil aceitável",
@@ -470,7 +475,8 @@
"pg_tile_matched_tip": "Ciclos corretamente atribuídos a um programa conhecido",
"pg_time_left": "Tempo restante",
"pg_weak_match": "Correspondência fraca",
"phase_name": "Nome",
"phase_name": "Nome da fase",
"name": "Nome",
"phase_timeline": "Cronologia de fases",
"power": "Potência",
"profile": "Perfil",
@@ -1039,7 +1045,12 @@
"odometer_intro": "Este total só aumenta. Eliminar o registo de um ciclo não o altera e continua a contar além do limite do histórico guardado, para que os lembretes de manutenção se mantenham corretos.",
"odometer_correct_hint": "Defina este valor se o WashData registou um ciclo que nunca aconteceu, ou se o eletrodoméstico já tinha realizado ciclos antes de o WashData ser instalado.",
"no_reminders_set": "Ainda não há lembretes de manutenção definidos.",
"toast_program_armed": "Programa preparado para o próximo ciclo: {program}"
"toast_program_armed": "Programa preparado para o próximo ciclo: {program}",
"standby_above_stop_title": "Ainda consome {idle} W quando o ciclo terminou",
"standby_above_stop_sub": "{n} dos últimos {total} ciclos. O Atraso de Desligamento só começa quando a potência desce abaixo do Limiar de Paragem ({stop} W), pelo que os ciclos terminam tarde ou têm de ser parados à mão.",
"advisory_duration_outlier": "{n} ciclo(s) rotulados como '{name}' estão demasiado longe da sua duração habitual para alguma vez lhe corresponderem (pior caso: {ratio}x). Provavelmente estão mal rotulados, ou uma gravação captou duas execuções. Volte a rotulá-los ou divida-os para deixarem de distorcer a estimativa de tempo deste programa.",
"toast_range_required": "Defina um intervalo De/Até válido",
"no_profiles_option": "(sem perfis, adicione um)"
},
"pg_desc": {
"dishwasher_end_spike_quiet_release": "Máquina de lavar louça: segundos de silêncio após a duração esperada antes de libertar a espera da descarga de fim de ciclo",
@@ -1063,7 +1074,7 @@
"duration_weight": "Fase 4: o quanto a concordância de duração afeta a pontuação final (predefinição 0,22)",
"energy_weight": "Fase 4: o quanto a concordância de energia afeta a pontuação final (predefinição 0,22)",
"profile_match_min_duration_ratio": "Fase 1: duração mínima do ciclo como fração da duração do perfil; predefinição 0,1 (10%)",
"profile_match_max_duration_ratio": "Fase 1: duração máxima do ciclo como fração da duração do perfil; predefinição 1,5 (150%)",
"profile_match_max_duration_ratio": "Fase 1: duração máxima do ciclo como fração da duração do perfil; predefinição 1,8 (180%)",
"keep_min_score": "Fase 2: pontuação mínima para permanecer na disputa; a predefinição 0,1 admite correspondências fracas, as fases seguintes escolhem a melhor",
"dtw_blend": "Fase 3: 0 = apenas pontuação central, 1 = apenas pontuação DTW, 0,5 = mistura equilibrada (predefinição)",
"dtw_ensemble_w": "Fase 3 (modo ensemble): peso em L1 escalado vs DTW derivativo; a predefinição 0,7 favorece a sensibilidade ao nível",
@@ -1398,8 +1409,8 @@
"label": "Atraso de Lembrete de Descarga"
},
"notify_unload_repeat": {
"doc": "Continua a reenviar o lembrete de descarga a cada \"Atraso de Lembrete de Descarga\" minutos até abrir a porta ou tocar em \"Parar de lembrar\" na notificação. Requer uma Entidade do Sensor de Porta (o próprio lembrete a exige). O botão de dispensar funciona nas notificações do aplicativo complementar (móvel) do Home Assistant.",
"label": "Repetir Até a Porta Abrir"
"doc": "Continua a reenviar o lembrete de descarga a cada \"Atraso de Lembrete de Descarga\" minutos até a descarga ser confirmada ou tocar em \"Parar de lembrar\" na notificação. Requer uma forma de confirmar a descarga: uma Entidade do Sensor de Porta, uma Entidade de Confirmação de Descarga ou a opção \"Confirmar a Descarga Manualmente\" abaixo. O botão de dispensar funciona nas notificações da aplicação complementar (móvel) do Home Assistant.",
"label": "Repetir Até Confirmar a Descarga"
},
"off_delay": {
"doc": "Tempo de espera após a queda de energia antes de declarar o término do ciclo. Se a energia for retomada dentro desta janela, o ciclo continua perfeitamente - isso faz a ponte entre as etapas de lavagem. As máquinas de lavar louça têm longas fases de secagem (desligadas por 20-60 minutos), portanto o atraso no desligamento deve exceder esse valor para manter toda a lavagem + secagem em um único ciclo.",
@@ -1607,6 +1618,18 @@
"energy_price_dynamic": {
"label": "Custo Ponderado no Tempo",
"doc": "Cobra cada ciclo ao preço em vigor em cada momento em que decorreu, em vez do preço único do instante em que terminou. Só se aplica a uma entidade de preço (um preço estático não pode variar). Não precisa de configuração adicional: o preço é acompanhado em direto enquanto o ciclo decorre e Processar histórico, em Diagnósticos, recalcula o custo dos ciclos antigos a partir do recorder. Desativado = o comportamento clássico, um preço fixado no fim."
},
"unload_confirm_entity": {
"label": "Entidade de Confirmação de Descarga",
"doc": "Entidade opcional cuja ativação significa que a carga foi retirada, para máquinas que não podem ter um sensor de porta. Serve qualquer tipo: um botão Zigbee ou NFC, um ajudante input_button, um sensor de movimento em frente à máquina, uma cena. Qualquer mudança de estado conta como confirmação, exceto desligar-se ou ficar indisponível. Definir isto também ativa o lembrete de descarga num dispositivo sem Entidade do Sensor de Porta."
},
"unload_track_without_door": {
"label": "Confirmar a Descarga Manualmente",
"doc": "Executa o lembrete de descarga num dispositivo sem Entidade do Sensor de Porta e sem Entidade de Confirmação de Descarga, confirmando a descarga a partir da sua própria automação: prima a entidade de botão \"Marcar como descarregado\" ou chame o serviço ha_washdata.mark_unloaded. É ignorado quando está definida uma Entidade do Sensor de Porta, que já fornece o sinal."
},
"notify_icon_color": {
"label": "Cor da Notificação",
"doc": "Cor de destaque para as notificações deste eletrodoméstico, para distinguir num relance uma máquina de lavar roupa, uma máquina de secar e uma máquina de lavar louça. O iOS colore o ícone e a barra de progresso da Atividade em Direto no Ecrã Bloqueado e na Dynamic Island. O Android recebe a mesma cor, mas o que faz com ela depende do telemóvel: alguns colorem o ícone e muitos (em especial o Samsung One UI) ignoram-na por completo. Ambos entendem o formato hexadecimal (por exemplo, #03A9F4). Deixe em branco para a predefinição da plataforma."
}
},
"setting_group": {
@@ -1700,7 +1723,7 @@
"all_statuses": "Todos os estados",
"ambiguous": "Ambíguo",
"auto_detect": "Detectar automaticamente",
"clear": "Limpar",
"clear": "Inequívoco",
"completed": "Concluído",
"fine_tuning": "a ajustar…",
"force_stopped": "Parado forçosamente",
@@ -1839,6 +1862,7 @@
"store_share_failed": "Falha na partilha: {error}",
"store_shared": "Partilhado na loja da comunidade, a aguardar revisão.",
"appliance_added": "Electrodoméstico adicionado, a aguardar aprovação",
"contrib_device_changed": "Adicionado à loja da comunidade, mas o dispositivo seleccionado mudou enquanto o formulário estava aberto. Seleccione-o nas Definições.",
"thanks_confirming": "Obrigado por confirmar",
"device_approved": "Aprovado pela comunidade",
"rating_saved": "Avaliação de qualidade guardada",
@@ -1862,7 +1886,9 @@
"unlabel_done": "Rótulo removido de {count} ciclo(s)",
"unlabel_failed": "Falha ao remover o rótulo: {error}",
"odometer_saved": "Total de ciclos guardado",
"odometer_save_failed": "Não foi possível guardar o total: {error}"
"odometer_save_failed": "Não foi possível guardar o total: {error}",
"store_pick_profile_share": "Escolha um perfil onde partilhar",
"remove_failed": "Falha ao remover: {error}"
},
"suggestion": {
"both_agree": "WashData recomenda",
@@ -1962,6 +1988,7 @@
"summary": "Excluído(s) {total} ciclo(s) mal detetado(s): {parts}.",
"reason": {
"force_stopped": "parados forçosamente",
"user_stopped": "parados manualmente",
"interrupted": "interrompidos",
"incomplete": "incompletos",
"noise": "ruído",
@@ -2187,5 +2214,10 @@
"hold_action": "Ação ao manter premido",
"double_tap_action": "Ação ao tocar duas vezes"
}
},
"note": {
"off_delay": {
"effective": "Um ciclo terminado espera na realidade {eff} s, e não {delay} s: o Intervalo Mínimo de Desligamento ({gap} s) é mais longo e tem prioridade."
}
}
}
@@ -35,7 +35,8 @@
"not_importable": "n/a aici",
"exists": "există deja",
"backfilled_tip": "Detectat într-un istoric de putere importat. Influențează doar potrivirea programelor, nu este contabilizat în statistici.",
"armed": "(se aplică ciclului următor)"
"armed": "(se aplică ciclului următor)",
"duration_outlier": "{n} ciclu(ri) cu durată neobișnuită"
},
"btn": {
"reset_zoom": "Resetați zoomul",
@@ -56,7 +57,7 @@
"auto_label_cycles_tip": "Atribuiți automat nume de profil ciclurilor neetichetate a căror încredere de potrivire depășește pragul",
"cancel": "Anulați",
"clear_debug": "Curățați datele de depanare",
"clear_splits": "Curățați",
"clear_splits": "Ștergeți",
"close": "Închide",
"compare": "Comparați",
"confirm": "Confirmați",
@@ -208,7 +209,10 @@
"hist_scan": "Căutați cicluri",
"hist_import_n": "Importați {n} cicluri",
"hist_goto_cycles": "Vedeți ciclurile",
"save_total_cycles": "Salvați totalul"
"save_total_cycles": "Salvați totalul",
"clear": "Ștergeți",
"add_brand": "Adăugați marcă",
"add_appliance": "Adăugați aparat"
},
"conflict": {
"anti_wrinkle_exit": {
@@ -330,7 +334,8 @@
"community_store": "Magazinul comunității",
"online_account": "Magazinul comunității și funcții online",
"import_power_history": "Importați istoricul de putere",
"service_status": "Starea întreținerii"
"service_status": "Starea întreținerii",
"online_account_tab": "Online și comunitate"
},
"health": {
"fair": "Calitate acceptabilă a profilului",
@@ -470,7 +475,8 @@
"pg_tile_matched_tip": "Cicluri atribuite corect unui program cunoscut",
"pg_time_left": "Timp rămas",
"pg_weak_match": "Potrivire slabă",
"phase_name": "Nume",
"phase_name": "Numele fazei",
"name": "Nume",
"phase_timeline": "Cronologie faze",
"power": "Putere",
"profile": "Profil",
@@ -1039,7 +1045,12 @@
"odometer_intro": "Acest total doar crește. Ștergerea înregistrării unui ciclu nu îl modifică și continuă să numere dincolo de limita istoricului păstrat, astfel încât mementourile de service rămân corecte.",
"odometer_correct_hint": "Setați această valoare dacă WashData a înregistrat un ciclu care nu a avut loc niciodată sau dacă aparatul efectuase deja cicluri înainte de instalarea WashData.",
"no_reminders_set": "Încă nu este setat niciun memento de service.",
"toast_program_armed": "Program pregătit pentru ciclul următor: {program}"
"toast_program_armed": "Program pregătit pentru ciclul următor: {program}",
"standby_above_stop_title": "Încă consumă {idle} W când ciclul s-a încheiat",
"standby_above_stop_sub": "{n} din ultimele {total} cicluri. Întârzierea la Oprire începe abia după ce puterea scade sub Pragul de Oprire ({stop} W), așa că ciclurile se termină târziu sau trebuie oprite manual.",
"advisory_duration_outlier": "{n} ciclu(ri) etichetate '{name}' sunt prea departe de durata sa obișnuită ca să se mai potrivească vreodată cu el (cel mai rău: {ratio}x). Probabil sunt etichetate greșit sau o înregistrare a surprins două rulări. Reetichetați-le sau împărțiți-le, ca să nu mai denatureze estimarea de timp a acestui program.",
"toast_range_required": "Setați un interval De la/Până la valid",
"no_profiles_option": "(niciun profil, adăugați unul)"
},
"phase_desc": {
"anti_crease": "Tumblări scurte ocazionale după finalizare pentru a reduce șifonarea.",
@@ -1366,8 +1377,8 @@
"label": "Întârziere Reamintire Descărcare"
},
"notify_unload_repeat": {
"doc": "Continuă să retrimită mementoul de descărcare la fiecare \"Întârziere Reamintire Descărcare\" minute până când deschizi ușa sau atingi \"Oprește mementourile\" în notificare. Necesită o Entitate Senzor Ușă (mementoul în sine o necesită). Butonul de respingere funcționează pe notificările aplicației complementare (mobile) Home Assistant.",
"label": "Repetă Până la Deschiderea Ușii"
"doc": "Continuă să retrimită mementoul de descărcare la fiecare \"Întârziere Reamintire Descărcare\" minute până când descărcarea este confirmată sau atingi \"Oprește mementourile\" în notificare. Necesită o modalitate de a confirma descărcarea: o Entitate Senzor Ușă, o Entitate Confirmare Descărcare sau opțiunea \"Confirmă Descărcarea Manual\" de mai jos. Butonul de respingere funcționează pe notificările aplicației complementare (mobile) Home Assistant.",
"label": "Repetă Până la Confirmarea Descărcării"
},
"off_delay": {
"doc": "Timp de așteptat după căderea energiei înainte de a declara ciclul încheiat. Dacă alimentarea se reia în această fereastră, ciclul continuă fără întreruperi - acest lucru face legătura între etapele de spălare. Mașinile de spălat vase au faze lungi de uscare (oprire pentru 20-60 de minute), așa că întârzierea de oprire trebuie să depășească aceasta pentru a menține întreaga spălare+uscare ca un singur ciclu.",
@@ -1575,6 +1586,18 @@
"energy_price_dynamic": {
"label": "Cost Ponderat în Timp",
"doc": "Taxează fiecare ciclu la prețul în vigoare în fiecare moment în care a rulat, în loc de prețul unic din momentul în care s-a terminat. Se aplică doar unei entități de preț (un preț static nu poate varia). Nu necesită configurare suplimentară: prețul este urmărit în timp real cât timp ciclul rulează, iar Procesare istoric, din Diagnostice, recalculează costul ciclurilor mai vechi pornind de la recorder. Dezactivat = comportamentul clasic, un singur preț fixat la final."
},
"unload_confirm_entity": {
"label": "Entitate Confirmare Descărcare",
"doc": "Entitate opțională a cărei activare înseamnă că încărcătura a fost scoasă, pentru mașinile care nu pot avea un senzor de ușă. Merge orice tip: un buton Zigbee sau NFC, un ajutor input_button, un senzor de mișcare în fața mașinii, o scenă. Orice schimbare de stare contează ca o confirmare, cu excepția opririi și a trecerii în indisponibil. Setarea acesteia activează și mementoul de descărcare pe un dispozitiv fără Entitate Senzor Ușă."
},
"unload_track_without_door": {
"label": "Confirmă Descărcarea Manual",
"doc": "Rulează mementoul de descărcare pe un dispozitiv fără Entitate Senzor Ușă și fără Entitate Confirmare Descărcare, iar descărcarea o confirmi din propria automatizare: apasă entitatea buton \"Marcați ca descărcat\" sau apelează serviciul ha_washdata.mark_unloaded. Se ignoră când este setată o Entitate Senzor Ușă, care oferă deja semnalul."
},
"notify_icon_color": {
"label": "Culoare Notificare",
"doc": "Culoare de accent pentru notificările acestui aparat, astfel încât o mașină de spălat rufe, un uscător și o mașină de spălat vase să poată fi deosebite dintr-o privire. iOS colorează pictograma și bara de progres a Activității live de pe ecranul blocat și din Dynamic Island. Android primește aceeași culoare, dar ce face cu ea depinde de telefon: unele colorează pictograma, iar multe (în special Samsung One UI) o ignoră complet. Formatul hexazecimal (de exemplu, #03A9F4) este înțeles de ambele. Lăsați necompletat pentru valoarea prestabilită a platformei."
}
},
"setting_group": {
@@ -1668,7 +1691,7 @@
"all_statuses": "Toate stările",
"ambiguous": "Ambiguu",
"auto_detect": "Detectare automată",
"clear": "Curățați",
"clear": "Clar",
"completed": "Finalizat",
"fine_tuning": "se ajustează…",
"force_stopped": "Oprit forțat",
@@ -1807,6 +1830,7 @@
"store_share_failed": "Partajarea a eșuat: {error}",
"store_shared": "Partajat în magazinul comunității, în așteptarea examinării.",
"appliance_added": "Aparat adăugat, în așteptarea aprobării",
"contrib_device_changed": "Adăugat în magazinul comunității, dar dispozitivul selectat s-a schimbat în timp ce formularul era deschis. Selectați-l în Setări.",
"thanks_confirming": "Vă mulțumim pentru confirmare",
"device_approved": "Aprobat de comunitate",
"rating_saved": "Evaluarea calității a fost salvată",
@@ -1830,7 +1854,9 @@
"unlabel_done": "Etichetă eliminată de la {count} ciclu(ri)",
"unlabel_failed": "Eliminarea etichetei a eșuat: {error}",
"odometer_saved": "Totalul ciclurilor a fost salvat",
"odometer_save_failed": "Nu s-a putut salva totalul: {error}"
"odometer_save_failed": "Nu s-a putut salva totalul: {error}",
"store_pick_profile_share": "Alegeți un profil în care să partajați",
"remove_failed": "Eliminarea a eșuat: {error}"
},
"suggestion": {
"both_agree": "WashData recomandă",
@@ -1930,6 +1956,7 @@
"summary": "Excluse {total} cicluri detectate greșit: {parts}.",
"reason": {
"force_stopped": "oprite forțat",
"user_stopped": "oprite manual",
"interrupted": "întrerupte",
"incomplete": "incomplete",
"noise": "zgomot",
@@ -1976,7 +2003,7 @@
"duration_weight": "Etapa 4: cât de puternic influențează acordul de durată scorul final (implicit 0.22)",
"energy_weight": "Etapa 4: cât de puternic influențează acordul de energie scorul final (implicit 0.22)",
"profile_match_min_duration_ratio": "Etapa 1: lungimea minimă a ciclului ca fracție din durata profilului; implicit 0.1 (10%)",
"profile_match_max_duration_ratio": "Etapa 1: lungimea maximă a ciclului ca fracție din durata profilului; implicit 1.5 (150%)",
"profile_match_max_duration_ratio": "Etapa 1: lungimea maximă a ciclului ca fracție din durata profilului; implicit 1.8 (180%)",
"keep_min_score": "Etapa 2: prag minim de scor pentru a rămâne în cursă; implicit 0.1 admite potriviri slabe, etapele ulterioare aleg cel mai bun",
"dtw_blend": "Etapa 3: 0 = doar scor de bază, 1 = doar scor DTW, 0.5 = amestec egal (implicit)",
"dtw_ensemble_w": "Etapa 3 (modul ansamblu): ponderea pe L1 scalat față de DTW derivativ; implicit 0.7 favorizează nivelul de putere",
@@ -2187,5 +2214,10 @@
"hold_action": "Acțiune la apăsare lungă",
"double_tap_action": "Acțiune la atingere dublă"
}
},
"note": {
"off_delay": {
"effective": "Un ciclu încheiat așteaptă de fapt {eff} s, nu {delay} s: Pauza Minimă de Oprire ({gap} s) este mai lungă și are prioritate."
}
}
}
@@ -60,6 +60,7 @@
"fair_fit_tip": "Умеренная согласованность совпадений – некоторые циклы, назначенные этому профилю, имеют более низкие оценки достоверности. Пометьте больше циклов или перезапишите профиль, чтобы повысить точность.",
"feedback_requested": "Запрошен отзыв",
"golden_cycle": "Записанный эталонный цикл",
"duration_outlier": "Циклы необычной длительности: {n}",
"improving": "↗ улучшается",
"manual": "(выбрано вручную)",
"needs_review": "Требуется проверка",
@@ -256,7 +257,15 @@
"hist_scan": "Найти циклы",
"hist_import_n": "Импортировать циклы ({n})",
"hist_goto_cycles": "Показать циклы",
"save_total_cycles": "Сохранить общее количество"
"save_total_cycles": "Сохранить общее количество",
"clear": "Очистить",
"add_brand": "Добавить бренд",
"add_appliance": "Добавить устройство"
},
"note": {
"off_delay": {
"effective": "Завершённый цикл на самом деле ждёт {eff} с, а не {delay} с: Минимальная Пауза Выключения ({gap} с) длиннее и имеет приоритет."
}
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +387,8 @@
"community_store": "Магазин сообщества",
"online_account": "Магазин сообщества и онлайн-функции",
"import_power_history": "Импорт истории мощности",
"service_status": "Состояние обслуживания"
"service_status": "Состояние обслуживания",
"online_account_tab": "Онлайн и сообщество"
},
"health": {
"fair": "Приемлемое качество профиля",
@@ -519,7 +529,8 @@
"pg_tile_matched_tip": "Циклы, правильно отнесённые к известной программе",
"pg_time_left": "Осталось времени",
"pg_weak_match": "Слабое совпадение",
"phase_name": "Имя",
"phase_name": "Имя фазы",
"name": "Имя",
"phase_timeline": "Хронология фаз",
"power": "Мощность",
"profile": "Профиль",
@@ -1032,6 +1043,9 @@
"imported_readonly": "Импортировано из магазина сообщества. Показывается для справки и сопоставления. Не учитывается в вашей статистике и не может быть изменено.",
"merge_no_imports": "Импортированные записи нельзя объединить с реальным циклом. Снимите с них выбор, чтобы объединить.",
"store_enable_hint": "Включите онлайн-функции в настройках, чтобы просматривать и импортировать эталонные циклы сообщества.",
"advisory_duration_outlier": "Циклы с меткой '{name}' ({n}) слишком далеки от её обычной длительности, чтобы когда-либо с ней совпасть (наибольшее отклонение: {ratio}x). Вероятно, они помечены неверно или одна запись охватила два запуска. Поменяйте метку или разделите их, чтобы они перестали искажать оценку времени для этой программы.",
"standby_above_stop_title": "В момент завершения цикла всё ещё потребляется {idle} Вт",
"standby_above_stop_sub": "{n} из последних {total} циклов. Задержка Выключения начинает отсчитываться только после того, как мощность опустится ниже Порога Остановки ({stop} Вт), поэтому циклы завершаются с опозданием или их приходится останавливать вручную.",
"online_intro": "Делитесь эталонными циклами с другими пользователями WashData и загружайте их. Все онлайн-функции подключаются по желанию и по умолчанию отключены.",
"store_share_intro": "Загрузите этот эталонный цикл, чтобы другие пользователи с таким же устройством могли его использовать. Он проходит проверку перед публикацией.",
"online_intro_global": "Просматривайте эталонные записи и делитесь ими с другими пользователями WashData, а также подтверждайте записи об устройствах. Одно подключение действует для всей вашей интеграции WashData; бренд и модель устройства задаются для каждого устройства в разделе «Основные». Все онлайн-функции подключаются по желанию и по умолчанию отключены.",
@@ -1087,7 +1101,9 @@
"odometer_intro": "Это общее количество только растёт. Удаление записи о цикле его не меняет, а подсчёт продолжается и за пределами лимита хранимой истории, поэтому напоминания об обслуживании остаются верными.",
"odometer_correct_hint": "Задайте значение, если WashData записал цикл, которого не было, или если прибор уже выполнял циклы до установки WashData.",
"no_reminders_set": "Напоминания об обслуживании ещё не заданы.",
"toast_program_armed": "Программа выбрана для следующего цикла: {program}"
"toast_program_armed": "Программа выбрана для следующего цикла: {program}",
"toast_range_required": "Задайте корректный диапазон От/До",
"no_profiles_option": "(профилей пока нет, добавьте новый)"
},
"phase_desc": {
"anti_crease": "Периодические короткие вращения барабана после завершения для уменьшения складок.",
@@ -1410,8 +1426,8 @@
"label": "Задержка Напоминания о Выгрузке"
},
"notify_unload_repeat": {
"doc": "Продолжает отправлять напоминание о выгрузке каждые \"Задержка Напоминания о Выгрузке\" минут, пока вы не откроете дверцу или не нажмёте \"Не напоминать\" в уведомлении. Требуется Объект Датчика Дверцы (как и для самого напоминания). Кнопка отклонения работает в уведомлениях мобильного приложения Home Assistant (companion).",
"label": "Повторять, пока не откроется дверца"
"doc": "Продолжать отправлять напоминание о выгрузке каждые «Задержка Напоминания о Выгрузке» минут, пока выгрузка не будет подтверждена или пока вы не нажмёте «Не напоминать» в уведомлении. Нужен способ подтвердить выгрузку: Объект Датчика Дверцы, Объект Подтверждения Выгрузки или параметр «Подтверждать Выгрузку Вручную» ниже. Кнопка отклонения работает в уведомлениях мобильного приложения Home Assistant (companion).",
"label": "Повторять, пока не выгружено"
},
"off_delay": {
"doc": "Время ожидания после отключения питания, прежде чем объявить цикл завершенным. Если подача электроэнергии возобновится в пределах этого окна, цикл продолжится плавно – это устраняет паузы между этапами стирки. Посудомоечные машины имеют длительные фазы сушки (отключение питания на 20–60 минут), поэтому задержка выключения должна превышать это значение, чтобы вся стирка+сушка выполнялась как один цикл.",
@@ -1623,6 +1639,18 @@
"curve_preroll_seconds": {
"doc": "Сколько секунд показаний от прерванных попыток запуска WashData может перенести в начало кривой цикла. Кривая цикла обычно начинается с той попытки запуска, которая в итоге удалась, поэтому на машине, которая делает несколько проб, прежде чем выйти на режим - выбор программы, блокировка люка, первый набор воды, - первые минуты реальной работы отсутствуют в каждой кривой. Задайте значение чуть больше самой длинной такой раскачки вашей машины (300 с с запасом хватает большинству). 0 отключает эту функцию. Учтите, что её включение сдвигает записанное начало раньше, поэтому циклы, записанные до изменения и после него, несут разную длительность для одной и той же программы; выученные средние будут смещаться, пока старые циклы не выйдут из истории.",
"label": "Предзапись кривой"
},
"unload_confirm_entity": {
"label": "Объект Подтверждения Выгрузки",
"doc": "Необязательный объект, срабатывание которого означает, что содержимое извлечено; для машин, на которые нельзя установить датчик дверцы. Подойдёт любой: кнопка Zigbee или NFC, вспомогательный элемент input_button, датчик движения перед машиной, сцена. Подтверждением считается любое изменение состояния, кроме выключения и перехода в состояние «Недоступно». Установка этого объекта также включает напоминание о выгрузке на устройстве без Объекта Датчика Дверцы."
},
"unload_track_without_door": {
"label": "Подтверждать Выгрузку Вручную",
"doc": "Использовать напоминание о выгрузке на устройстве без Объекта Датчика Дверцы и без Объекта Подтверждения Выгрузки, подтверждая выгрузку из собственной автоматизации: нажмите объект-кнопку «Отметить как выгруженное» или вызовите службу ha_washdata.mark_unloaded. Игнорируется, если задан Объект Датчика Дверцы, который уже даёт такой сигнал."
},
"notify_icon_color": {
"label": "Цвет Уведомления",
"doc": "Акцентный цвет уведомлений этого прибора, чтобы стиральную, сушильную и посудомоечную машину можно было различить с первого взгляда. iOS окрашивает значок и индикатор выполнения Live Activity на экране блокировки и в Dynamic Island. Android получает тот же цвет, но что с ним делать, решает сам телефон: некоторые окрашивают значок, а многие (особенно Samsung One UI) полностью его игнорируют. Обе платформы понимают Hex (например, #03A9F4). Оставьте пустым, чтобы использовать цвет по умолчанию для платформы."
}
},
"setting_group": {
@@ -1716,7 +1744,7 @@
"all_statuses": "Все статусы",
"ambiguous": "Неоднозначно",
"auto_detect": "Авто-определение",
"clear": "Очистить",
"clear": "Однозначно",
"completed": "Завершено",
"fine_tuning": "точная настройка…",
"force_stopped": "Принудительно остановлено",
@@ -1854,6 +1882,7 @@
"store_no_appliance": "Сначала укажите бренд и модель устройства в настройках.",
"store_share_failed": "Не удалось поделиться: {error}",
"store_shared": "Опубликовано в магазине сообщества, ожидает проверки.",
"contrib_device_changed": "Добавлено в магазин сообщества, но выбранное устройство изменилось, пока форма была открыта. Выберите его в настройках.",
"appliance_added": "Устройство добавлено, ожидает одобрения",
"thanks_confirming": "Спасибо за подтверждение",
"device_approved": "Одобрено сообществом",
@@ -1878,7 +1907,9 @@
"unlabel_done": "Метка снята с {count} цикла(ов)",
"unlabel_failed": "Не удалось снять метку: {error}",
"odometer_saved": "Общее количество циклов сохранено",
"odometer_save_failed": "Не удалось сохранить общее количество: {error}"
"odometer_save_failed": "Не удалось сохранить общее количество: {error}",
"store_pick_profile_share": "Выберите профиль для публикации",
"remove_failed": "Не удалось удалить: {error}"
},
"suggestion": {
"both_agree": "WashData рекомендует",
@@ -1978,6 +2009,7 @@
"summary": "Исключено {total} ошибочно определённых циклов: {parts}.",
"reason": {
"force_stopped": "принудительно остановленные",
"user_stopped": "остановленные вручную",
"interrupted": "прерванные",
"incomplete": "незавершённые",
"noise": "шум",
@@ -2024,7 +2056,7 @@
"duration_weight": "Этап 4: насколько сильно совпадение по длительности влияет на итоговый балл (по умолчанию 0,22)",
"energy_weight": "Этап 4: насколько сильно совпадение по энергии влияет на итоговый балл (по умолчанию 0,22)",
"profile_match_min_duration_ratio": "Этап 1: минимальная длина цикла как доля длительности профиля; по умолчанию 0,1 (10%)",
"profile_match_max_duration_ratio": "Этап 1: максимальная длина цикла как доля длительности профиля; по умолчанию 1,5 (150%)",
"profile_match_max_duration_ratio": "Этап 1: максимальная длина цикла как доля длительности профиля; по умолчанию 1,8 (180%)",
"keep_min_score": "Этап 2: минимальный балл для участия в соревновании; по умолчанию 0,1 допускает слабые совпадения, поздние этапы выбирают лучшее",
"dtw_blend": "Этап 3: 0 = только основной балл, 1 = только балл DTW, 0,5 = равное смешивание (по умолчанию)",
"dtw_ensemble_w": "Этап 3 (режим ансамбля): вес масштабированного L1 против производного DTW; по умолчанию 0,7 отдаёт предпочтение уровню",
@@ -60,6 +60,7 @@
"fair_fit_tip": "Stredná konzistencia zhody – niektoré cykly priradené k tomuto profilu majú nižšie skóre spoľahlivosti. Označte viac cyklov alebo znova nahrajte profil, aby ste zlepšili presnosť.",
"feedback_requested": "Vyžiadaná spätná väzba",
"golden_cycle": "Zaznamenaný referenčný cyklus",
"duration_outlier": "Cykly s neobvyklou dĺžkou: {n}",
"improving": "↗ zlepšujúci sa",
"manual": "(vybrané ručne)",
"needs_review": "Vyžaduje preskúmanie",
@@ -256,7 +257,15 @@
"hist_scan": "Vyhľadať cykly",
"hist_import_n": "Importovať cykly: {n}",
"hist_goto_cycles": "Zobraziť cykly",
"save_total_cycles": "Uložiť celkový počet"
"save_total_cycles": "Uložiť celkový počet",
"clear": "Vymazať",
"add_brand": "Pridať značku",
"add_appliance": "Pridať spotrebič"
},
"note": {
"off_delay": {
"effective": "Dokončený cyklus v skutočnosti čaká {eff} s, nie {delay} s: Min. medzera vypnutia ({gap} s) je dlhšia a má prednosť."
}
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +387,8 @@
"community_store": "Komunitný obchod",
"online_account": "Komunitný obchod a online funkcie",
"import_power_history": "Import histórie výkonu",
"service_status": "Stav servisu"
"service_status": "Stav servisu",
"online_account_tab": "Online a komunita"
},
"health": {
"fair": "Prijateľná kvalita profilu",
@@ -519,7 +529,8 @@
"pg_tile_matched_tip": "Cykly správne priradené k známemu programu",
"pg_time_left": "Zostávajúci čas",
"pg_weak_match": "Slabá zhoda",
"phase_name": "Názov",
"phase_name": "Názov fázy",
"name": "Názov",
"phase_timeline": "Časová os fáz",
"power": "Výkon",
"profile": "Profil",
@@ -1032,6 +1043,9 @@
"imported_readonly": "Importované z komunitného obchodu. Zobrazuje sa pre referenciu a porovnávanie. Nezapočítava sa do vašich štatistík a nedá sa upravovať.",
"merge_no_imports": "Importované záznamy nemožno zlúčiť so skutočným cyklom. Zrušte ich výber, aby ste ich mohli zlúčiť.",
"store_enable_hint": "Povoľte online funkcie v Nastaveniach, aby ste mohli prehliadať a importovať referenčné cykly komunity.",
"advisory_duration_outlier": "Cykly označené ako '{name}' ({n}) sú príliš vzdialené od jeho obvyklej dĺžky, než aby sa s ním dali niekedy zhodovať (najväčšia odchýlka: {ratio}x). Pravdepodobne sú nesprávne označené alebo jeden záznam zachytil dva behy. Označte ich znova alebo ich rozdeľte, aby prestali skresľovať odhad času tohto programu.",
"standby_above_stop_title": "Pri ukončení cyklu stále odoberá {idle} W",
"standby_above_stop_sub": "{n} z posledných {total} cyklov. Oneskorenie vypnutia sa začne počítať až vtedy, keď príkon klesne pod Prah zastavenia ({stop} W), takže cykly končia neskoro alebo ich treba zastaviť ručne.",
"online_intro": "Zdieľajte a sťahujte referenčné cykly s ostatnými používateľmi WashData. Všetky online funkcie sú dobrovoľné a predvolene vypnuté.",
"store_share_intro": "Nahrajte tento referenčný cyklus, aby ho mohli používať ostatní s rovnakým spotrebičom. Pred zverejnením bude skontrolovaný.",
"online_intro_global": "Prehliadajte a zdieľajte referenčné záznamy s ostatnými používateľmi WashData a potvrdzujte záznamy spotrebičov. Jedno pripojenie platí pre celú vašu integráciu WashData; značka a model spotrebiča sa nastavujú pre každé zariadenie v sekcii Základné. Všetky online funkcie sú dobrovoľné a predvolene vypnuté.",
@@ -1087,7 +1101,9 @@
"odometer_intro": "Tento celkový počet iba rastie. Odstránenie záznamu o cykle ho nezmení a počíta ďalej aj za limitom uloženej histórie, takže pripomienky servisu zostávajú správne.",
"odometer_correct_hint": "Nastavte, ak WashData zaznamenalo cyklus, ktorý nikdy neprebehol, alebo ak spotrebič už absolvoval cykly pred inštaláciou WashData.",
"no_reminders_set": "Zatiaľ nie sú nastavené žiadne pripomienky servisu.",
"toast_program_armed": "Program nastavený pre ďalší cyklus: {program}"
"toast_program_armed": "Program nastavený pre ďalší cyklus: {program}",
"toast_range_required": "Nastavte platný rozsah Od/Do",
"no_profiles_option": "(zatiaľ žiadne profily, pridajte nový)"
},
"phase_desc": {
"anti_crease": "Príležitostné krátke vírenia po dokončení na predchádzanie krčeniu oblečenia.",
@@ -1410,8 +1426,8 @@
"label": "Oneskorenie pripomienky vyloženia"
},
"notify_unload_repeat": {
"doc": "Znova odosiela pripomienku vyloženia každých \"Oneskorenie pripomienky vyloženia\" minút, kým neotvoríte dvere alebo v oznámení neťuknete na \"Prestať pripomínať\". Vyžaduje Entitu senzora dverí (rovnako ako samotná pripomienka). Tlačidlo na zrušenie funguje pri oznámeniach mobilnej aplikácie Home Assistant (companion app).",
"label": "Opakovať, kým sa neotvoria dvere"
"doc": "Znova odosiela pripomienku vyloženia každých „Oneskorenie pripomienky vyloženia“ minút, kým sa vyloženie nepotvrdí alebo v oznámení neťuknete na „Prestať pripomínať“. Vyžaduje spôsob potvrdenia vyloženia: Entitu senzora dverí, Entitu potvrdenia vyloženia alebo nižšie uvedenú možnosť „Ručné potvrdenie vyloženia“. Tlačidlo na zrušenie funguje pri oznámeniach mobilnej aplikácie Home Assistant (companion app).",
"label": "Opakovať do vyloženia"
},
"off_delay": {
"doc": "Čas čakania po poklese napájania pred vyhlásením cyklu za ukončený. Ak sa v rámci tohto okna obnoví napájanie, cyklus plynule pokračuje – premosťuje to prestávky medzi fázami umývania. Umývačky riadu majú dlhé fázy sušenia (vypnutie na 20-60 minút), takže oneskorenie vypnutia musí prekročiť túto hodnotu, aby celé umývanie + suché zostalo ako jeden cyklus.",
@@ -1623,6 +1639,18 @@
"curve_preroll_seconds": {
"doc": "Koľko sekúnd meraní z neúspešných pokusov o spustenie môže WashData preniesť na začiatok krivky cyklu. Krivka cyklu obvykle začína pri pokuse o spustenie, ktorý nakoniec uspel, takže pri stroji, ktorý to pred rozbehnutím niekoľkokrát skúsi - voľba programu, zámok dverí, prvé napúšťanie - chýbajú v každej krivke prvé minúty skutočnej činnosti. Nastavte túto hodnotu o niečo vyššie, než je najdlhší takýto rozbeh vášho stroja (300 s väčšine bohato stačí). 0 funkciu vypne. Majte na pamäti, že zapnutie posunie zaznamenaný začiatok skôr, takže cykly zaznamenané pred zmenou a po nej nesú pre ten istý program rozdielne doby trvania; naučené priemery sa budú posúvať, kým staršie cykly nevypadnú z histórie.",
"label": "Nábeh krivky"
},
"unload_confirm_entity": {
"label": "Entita potvrdenia vyloženia",
"doc": "Voliteľná entita, ktorej aktivácia znamená, že náplň bola vybratá, pre spotrebiče, na ktoré sa nedá umiestniť senzor dverí. Funguje akýkoľvek typ: tlačidlo Zigbee alebo NFC, pomocník input_button, senzor pohybu pred spotrebičom, scéna. Za potvrdenie sa považuje každá zmena stavu okrem vypnutia a prechodu do stavu nedostupný. Nastavenie tejto možnosti zapne pripomienku vyloženia aj na zariadení bez Entity senzora dverí."
},
"unload_track_without_door": {
"label": "Ručné potvrdenie vyloženia",
"doc": "Spustí pripomienku vyloženia na zariadení bez Entity senzora dverí a bez Entity potvrdenia vyloženia, pričom vyloženie potvrdíte z vlastnej automatizácie: stlačte entitu tlačidla „Označiť ako vyložené“ alebo zavolajte službu ha_washdata.mark_unloaded. Ignoruje sa, keď je nastavená Entita senzora dverí, ktorá tento signál už poskytuje."
},
"notify_icon_color": {
"label": "Farba upozornenia",
"doc": "Farba zvýraznenia upozornení tohto spotrebiča, aby sa práčka, sušička a umývačka riadu dali na prvý pohľad rozlíšiť. iOS zafarbí ikonu a ukazovateľ priebehu Live Activity na zamknutej obrazovke a v Dynamic Island. Android dostane rovnakú farbu, ale čo s ňou urobí, závisí od telefónu: niektoré zafarbia ikonu a mnohé (najmä Samsung One UI) ju úplne ignorujú. Obe platformy rozumejú formátu Hex (napr. #03A9F4). Nechajte prázdne pre predvolenú farbu platformy."
}
},
"setting_group": {
@@ -1716,7 +1744,7 @@
"all_statuses": "Všetky stavy",
"ambiguous": "Nejednoznačný",
"auto_detect": "Automatické zisťovanie",
"clear": "Vymazať",
"clear": "Jednoznačný",
"completed": "Dokončené",
"fine_tuning": "dolaďovanie…",
"force_stopped": "Nútene zastavené",
@@ -1835,6 +1863,7 @@
"summary": "Vylúčených {total} chybne zistených cyklov: {parts}.",
"reason": {
"force_stopped": "nútene zastavené",
"user_stopped": "ručne zastavené",
"interrupted": "prerušené",
"incomplete": "neúplné",
"noise": "šum",
@@ -1965,6 +1994,7 @@
"store_no_appliance": "Najprv nastavte značku a model spotrebiča v Nastaveniach.",
"store_share_failed": "Zdieľanie zlyhalo: {error}",
"store_shared": "Zdieľané do komunitného obchodu, čaká na kontrolu.",
"contrib_device_changed": "Pridané do komunitného obchodu, ale vybraný spotrebič sa zmenil, kým bol formulár otvorený. Vyberte ho v Nastaveniach.",
"appliance_added": "Spotrebič pridaný, čaká na schválenie",
"thanks_confirming": "Ďakujeme za potvrdenie",
"device_approved": "Schválené komunitou",
@@ -1989,7 +2019,9 @@
"unlabel_done": "Odobratý štítok pre {count} cyklov",
"unlabel_failed": "Odobratie štítka zlyhalo: {error}",
"odometer_saved": "Celkový počet cyklov uložený",
"odometer_save_failed": "Celkový počet sa nepodarilo uložiť: {error}"
"odometer_save_failed": "Celkový počet sa nepodarilo uložiť: {error}",
"store_pick_profile_share": "Vyberte profil na zdieľanie",
"remove_failed": "Odstránenie zlyhalo: {error}"
},
"trend": {
"down": "Klesajúci trend",
@@ -2024,7 +2056,7 @@
"duration_weight": "Fáza 4: ako silno zhoda dĺžky trvania ovplyvňuje výsledné skóre (predvolené 0,22)",
"energy_weight": "Fáza 4: ako silno zhoda energie ovplyvňuje výsledné skóre (predvolené 0,22)",
"profile_match_min_duration_ratio": "Fáza 1: minimálna dĺžka cyklu ako zlomok dĺžky trvania profilu; predvolené 0,1 (10 %)",
"profile_match_max_duration_ratio": "Fáza 1: maximálna dĺžka cyklu ako zlomok dĺžky trvania profilu; predvolené 1,5 (150 %)",
"profile_match_max_duration_ratio": "Fáza 1: maximálna dĺžka cyklu ako zlomok dĺžky trvania profilu; predvolené 1,8 (180 %)",
"keep_min_score": "Fáza 2: minimálne skóre na zostanie v závode; predvolené 0,1 pripúšťa slabé zhody, neskoršie fázy vyberú najlepšie",
"dtw_blend": "Fáza 3: 0 = iba základné skóre, 1 = iba skóre DTW, 0,5 = rovnaké miešanie (predvolené)",
"dtw_ensemble_w": "Fáza 3 (režim kombinácie): váha škálovaného L1 oproti derivátneho DTW; predvolené 0,7 uprednostňuje úroveň",
@@ -60,6 +60,7 @@
"fair_fit_tip": "Zmerna doslednost ujemanja – nekateri cikli, dodeljeni temu profilu, imajo nižje ocene zaupanja. Označite več ciklov ali znova posnemite profil, da izboljšate natančnost.",
"feedback_requested": "Zahtevane povratne informacije",
"golden_cycle": "Posneti referenčni cikel",
"duration_outlier": "Cikli nenavadne dolžine: {n}",
"improving": "↗ se izboljšuje",
"manual": "(ročno izbrano)",
"needs_review": "Potrebuje pregled",
@@ -256,7 +257,15 @@
"hist_scan": "Poišči cikle",
"hist_import_n": "Uvozi cikle: {n}",
"hist_goto_cycles": "Pokaži cikle",
"save_total_cycles": "Shrani skupno število"
"save_total_cycles": "Shrani skupno število",
"clear": "Počisti",
"add_brand": "Dodaj znamko",
"add_appliance": "Dodaj napravo"
},
"note": {
"off_delay": {
"effective": "Končan cikel v resnici čaka {eff} s in ne {delay} s: Min. presledek izklopa ({gap} s) je daljši in ima prednost."
}
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +387,8 @@
"community_store": "Skupnostna trgovina",
"online_account": "Skupnostna trgovina in spletne funkcije",
"import_power_history": "Uvoz zgodovine moči",
"service_status": "Stanje servisa"
"service_status": "Stanje servisa",
"online_account_tab": "Splet in skupnost"
},
"health": {
"fair": "Sprejemljiva kakovost profila",
@@ -519,7 +529,8 @@
"pg_tile_matched_tip": "Cikli, pravilno pripisani znanemu programu",
"pg_time_left": "Preostali čas",
"pg_weak_match": "Šibko ujemanje",
"phase_name": "Ime",
"phase_name": "Ime faze",
"name": "Ime",
"phase_timeline": "Časovnica faz",
"power": "Moč",
"profile": "Profil",
@@ -1032,6 +1043,9 @@
"imported_readonly": "Uvoženo iz skupnostne trgovine. Prikazano za referenco in ujemanje. Ne šteje se v vašo statistiko in ga ni mogoče urejati.",
"merge_no_imports": "Uvoženih posnetkov ni mogoče združiti z resničnim ciklom. Za združitev prekličite njihovo izbiro.",
"store_enable_hint": "Omogočite spletne funkcije v Nastavitvah, da boste lahko brskali in uvažali referenčne cikle skupnosti.",
"advisory_duration_outlier": "Cikli, označeni kot '{name}' ({n}), so preveč oddaljeni od njegove običajne dolžine, da bi se z njim kdaj lahko ujemali (največje odstopanje: {ratio}x). Verjetno so napačno označeni ali pa je en posnetek zajel dva zagona. Znova jih označite ali jih razdelite, da nehajo popačiti oceno časa za ta program.",
"standby_above_stop_title": "Ob koncu cikla še vedno porablja {idle} W",
"standby_above_stop_sub": "{n} od zadnjih {total} ciklov. Zamuda izklopa se začne šteti šele, ko moč pade pod Prag zaustavitve ({stop} W), zato se cikli končajo z zamudo ali jih je treba ustaviti ročno.",
"online_intro": "Delite in prenašajte referenčne cikle z drugimi uporabniki WashData. Vse spletne funkcije so prostovoljne in privzeto izklopljene.",
"store_share_intro": "Naložite ta referenčni cikel, da ga bodo lahko uporabljali drugi z enako napravo. Pred javno objavo je pregledan.",
"online_intro_global": "Brskajte in delite referenčne posnetke z drugimi uporabniki WashData ter potrjujte vnose naprav. Ena povezava velja za celotno integracijo WashData; znamka in model naprave se nastavita za vsako napravo posebej v razdelku Osnovno. Vse spletne funkcije so prostovoljne in privzeto izklopljene.",
@@ -1087,7 +1101,9 @@
"odometer_intro": "To skupno število samo narašča. Brisanje zapisa o ciklu ga ne spremeni in šteje naprej tudi prek omejitve shranjene zgodovine, zato opomniki za servis ostanejo pravilni.",
"odometer_correct_hint": "Nastavite to, če je WashData zabeležil cikel, ki se nikoli ni zgodil, ali če je naprava opravila cikle že pred namestitvijo WashData.",
"no_reminders_set": "Opomniki za servis še niso nastavljeni.",
"toast_program_armed": "Program nastavljen za naslednji cikel: {program}"
"toast_program_armed": "Program nastavljen za naslednji cikel: {program}",
"toast_range_required": "Nastavite veljaven obseg Od/Do",
"no_profiles_option": "(ni profilov, dodajte novega)"
},
"phase_desc": {
"anti_crease": "Občasni kratki premiki po zaključku za preprečevanje gub na oblačilih.",
@@ -1414,8 +1430,8 @@
"label": "Zamuda opomnika za raztovarjanje"
},
"notify_unload_repeat": {
"doc": "Znova pošilja opomnik za raztovarjanje vsakih \"Zamuda opomnika za raztovarjanje\" minut, dokler ne odprete vrat ali v obvestilu ne tapnete \"Nehaj opominjati\". Zahteva Entiteto senzorja vrat (tako kot sam opomnik). Gumb za opustitev deluje pri obvestilih mobilne aplikacije Home Assistant (companion app).",
"label": "Ponavljaj, dokler se vrata ne odprejo"
"doc": "Znova pošilja opomnik za raztovarjanje vsakih „Zamuda opomnika za raztovarjanje“ minut, dokler raztovarjanje ni potrjeno ali v obvestilu ne tapnete „Nehaj opominjati“. Potrebuje način za potrditev raztovarjanja: Entiteto senzorja vrat, Entiteto potrditve raztovarjanja ali spodnjo možnost „Ročna potrditev raztovarjanja“. Gumb za opustitev deluje pri obvestilih mobilne aplikacije Home Assistant (companion app).",
"label": "Ponavljaj do raztovarjanja"
},
"off_delay": {
"doc": "Čas za čakanje po padcu moči, preden razglasite, da je cikel končan. Če se napajanje ponovno vzpostavi v tem oknu, se cikel nemoteno nadaljuje - to premosti premore med stopnjami pranja. Pomivalni stroji imajo dolge faze sušenja (izklop za 20–60 min), zato mora biti zakasnitev izklopa daljša od te, da ostane celotno pranje+sušenje kot en cikel.",
@@ -1623,6 +1639,18 @@
"energy_price_dynamic": {
"label": "Časovno tehtani stroški",
"doc": "Vsak cikel obračuna po ceni, ki je veljala v vsakem trenutku njegovega teka, namesto po eni sami ceni ob zaključku. Velja samo za entiteto cene (statična cena se ne more spreminjati). Ne potrebuje dodatnih nastavitev: cena se med ciklom spremlja v živo, Obdelava zgodovine v Diagnostiki pa starejšim ciklom znova izračuna stroške iz recorderja. Izklopljeno = klasično vedenje, ena cena, zamrznjena ob koncu."
},
"unload_confirm_entity": {
"label": "Entiteta potrditve raztovarjanja",
"doc": "Izbirna entiteta, katere sprožitev pomeni, da je bila vsebina vzeta iz naprave, za stroje, na katere ni mogoče namestiti senzorja vrat. Deluje katera koli vrsta: gumb Zigbee ali NFC, pomočnik input_button, senzor gibanja pred strojem, scena. Kot potrditev šteje vsaka sprememba stanja, razen izklopa in prehoda v nedosegljivo stanje. Ta nastavitev omogoči opomnik za raztovarjanje tudi na napravi brez Entitete senzorja vrat."
},
"unload_track_without_door": {
"label": "Ročna potrditev raztovarjanja",
"doc": "Zažene opomnik za raztovarjanje na napravi brez Entitete senzorja vrat in brez Entitete potrditve raztovarjanja, raztovarjanje pa potrdite iz lastne avtomatizacije: pritisnite entiteto gumba „Označi kot raztovorjeno“ ali pokličite storitev ha_washdata.mark_unloaded. Prezrto, kadar je nastavljena Entiteta senzorja vrat, ki ta signal že zagotavlja."
},
"notify_icon_color": {
"label": "Barva obvestila",
"doc": "Poudarna barva obvestil te naprave, da so pralni stroj, sušilni stroj in pomivalni stroj razpoznavni na prvi pogled. iOS obarva ikono in vrstico napredka Live Activity na zaklenjenem zaslonu in v Dynamic Island. Android prejme enako barvo, a kaj bo z njo naredil, je odvisno od telefona: nekateri obarvajo ikono, mnogi (zlasti Samsung One UI) pa jo povsem prezrejo. Obe platformi razumeta Hex (npr. #03A9F4). Pustite prazno za privzeto barvo platforme."
}
},
"setting_group": {
@@ -1716,7 +1744,7 @@
"all_statuses": "Vsa stanja",
"ambiguous": "Dvoumno",
"auto_detect": "Samodejno zaznaj",
"clear": "Počisti",
"clear": "Nedvoumno",
"completed": "Zaključeno",
"fine_tuning": "natančno prilagajanje…",
"force_stopped": "Prisilno ustavljeno",
@@ -1835,6 +1863,7 @@
"summary": "Izključeno {total} napačno zaznanih ciklov: {parts}.",
"reason": {
"force_stopped": "prisilno ustavljeni",
"user_stopped": "ročno ustavljeni",
"interrupted": "prekinjeni",
"incomplete": "nepopolni",
"noise": "šum",
@@ -1965,6 +1994,7 @@
"store_no_appliance": "Najprej nastavite znamko in model naprave v Nastavitvah.",
"store_share_failed": "Deljenje ni uspelo: {error}",
"store_shared": "Deljeno v skupnostno trgovino, čaka na pregled.",
"contrib_device_changed": "Dodano v skupnostno trgovino, vendar se je izbrana naprava spremenila, medtem ko je bil obrazec odprt. Izberite jo v Nastavitvah.",
"appliance_added": "Naprava dodana, čaka na odobritev",
"thanks_confirming": "Hvala za potrditev",
"device_approved": "Odobreno s strani skupnosti",
@@ -1989,7 +2019,9 @@
"unlabel_done": "Odstranjena oznaka za {count} ciklov",
"unlabel_failed": "Odstranjevanje oznake ni uspelo: {error}",
"odometer_saved": "Skupno število ciklov shranjeno",
"odometer_save_failed": "Skupnega števila ni bilo mogoče shraniti: {error}"
"odometer_save_failed": "Skupnega števila ni bilo mogoče shraniti: {error}",
"store_pick_profile_share": "Izberite profil za deljenje",
"remove_failed": "Odstranjevanje ni uspelo: {error}"
},
"trend": {
"down": "Padajoči trend",
@@ -2024,7 +2056,7 @@
"duration_weight": "Stopnja 4: kako močno ujemanje trajanja vpliva na končno oceno (privzeto 0,22)",
"energy_weight": "Stopnja 4: kako močno ujemanje energije vpliva na končno oceno (privzeto 0,22)",
"profile_match_min_duration_ratio": "Stopnja 1: minimalna dolžina cikla kot del trajanja profila; privzeto 0,1 (10%)",
"profile_match_max_duration_ratio": "Stopnja 1: maksimalna dolžina cikla kot del trajanja profila; privzeto 1,5 (150%)",
"profile_match_max_duration_ratio": "Stopnja 1: maksimalna dolžina cikla kot del trajanja profila; privzeto 1,8 (180%)",
"keep_min_score": "Stopnja 2: najnižja ocena za ostanek v izboru; privzeto 0,1 sprejme šibka ujemanja, kasnejše stopnje izberejo najboljše",
"dtw_blend": "Stopnja 3: 0 = samo osnovna ocena, 1 = samo DTW ocena, 0,5 = enakomerno mešanje (privzeto)",
"dtw_ensemble_w": "Stopnja 3 (kombinirani način): utež skaliranega L1 v primerjavi z izvedenim DTW; privzeto 0,7 daje prednost ravni",
@@ -83,7 +83,8 @@
"not_importable": "s'aplikohet këtu",
"exists": "ekziston",
"backfilled_tip": "Zbuluar në historikun e importuar të fuqisë. Ndikon vetëm në përputhjen e programeve, nuk llogaritet në statistika.",
"armed": "(zbatohet për ciklin tjetër)"
"armed": "(zbatohet për ciklin tjetër)",
"duration_outlier": "{n} cikël(e) me kohëzgjatje të pazakontë"
},
"btn": {
"reset_zoom": "Rivendos zmadhimin",
@@ -256,7 +257,10 @@
"hist_scan": "Kërko cikle",
"hist_import_n": "Importo {n} cikle",
"hist_goto_cycles": "Shfaq ciklet",
"save_total_cycles": "Ruaj totalin"
"save_total_cycles": "Ruaj totalin",
"clear": "Pastro",
"add_brand": "Shto markë",
"add_appliance": "Shto pajisje"
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +382,8 @@
"community_store": "Dyqani i Komunitetit",
"online_account": "Dyqani i Komunitetit dhe veçoritë online",
"import_power_history": "Importo historikun e fuqisë",
"service_status": "Statusi i mirëmbajtjes"
"service_status": "Statusi i mirëmbajtjes",
"online_account_tab": "Online dhe komuniteti"
},
"health": {
"fair": "Cilësi e pranueshme e profilit",
@@ -518,7 +523,8 @@
"pg_tile_matched_tip": "Cikle të atribuara saktë një programi të njohur",
"pg_time_left": "Koha e mbetur",
"pg_weak_match": "Përputhje e dobët",
"phase_name": "Emri",
"phase_name": "Emri i fazës",
"name": "Emri",
"phase_timeline": "Kronologjia e fazave",
"power": "Fuqia",
"profile": "Profili",
@@ -1087,7 +1093,12 @@
"odometer_intro": "Ky total vetëm rritet. Fshirja e regjistrimit të një cikli nuk e ndryshon dhe vazhdon të numërojë përtej kufirit të historikut të ruajtur, kështu që kujtesat e servisit mbeten të sakta.",
"odometer_correct_hint": "Vendosni këtë vlerë nëse WashData regjistroi një cikël që nuk ndodhi kurrë, ose nëse pajisja kishte kryer cikle përpara se WashData të instalohej.",
"no_reminders_set": "Nuk është caktuar ende asnjë kujtesë servisi.",
"toast_program_armed": "Programi u caktua për ciklin tjetër: {program}"
"toast_program_armed": "Programi u caktua për ciklin tjetër: {program}",
"standby_above_stop_title": "Ende tërheq {idle} W kur cikli përfundoi",
"standby_above_stop_sub": "{n} nga {total} ciklet e fundit. Vonesa e fikjes fillon vetëm pasi fuqia bie nën Pragun e ndalimit ({stop} W), prandaj ciklet përfundojnë me vonesë ose duhen ndalur me dorë.",
"advisory_duration_outlier": "{n} cikël(e) të etiketuar si '{name}' janë shumë larg kohëzgjatjes së tij të zakonshme për t'u përputhur ndonjëherë me të (rasti më i keq: {ratio}x). Ka të ngjarë të jenë etiketuar gabimisht, ose një regjistrim i vetëm ka kapur dy ekzekutime. Rietiketojini ose ndajini, që të mos e shtrembërojnë më vlerësimin e kohës së këtij programi.",
"toast_range_required": "Vendosni një interval të vlefshëm Nga/Deri",
"no_profiles_option": "(asnjë profil, shtoni një)"
},
"phase_desc": {
"anti_crease": "Rrotullime të shkurtra të herëpashershme pas përfundimit për të reduktuar rrudhat.",
@@ -1410,8 +1421,8 @@
"label": "Vonesë njoftimi për shkarkim"
},
"notify_unload_repeat": {
"doc": "Ridërgon kujtesën për shkarkim çdo \"Vonesë njoftimi për shkarkim\" minuta, derisa të hapni derën ose të prekni \"Ndalo kujtesat\" në njoftim. Kërkon Entitetin e sensorit të derës (ashtu si edhe vetë kujtesa). Butoni i largimit funksionon në njoftimet e aplikacionit-shoqërues (celular) të Home Assistant.",
"label": "Përsërit derisa të hapet dera"
"doc": "Ridërgon kujtesën për shkarkim çdo \"Vonesë njoftimi për shkarkim\" minuta, derisa shkarkimi të konfirmohet ose të prekni \"Ndalo kujtesat\" në njoftim. Nevojitet një mënyrë për të konfirmuar shkarkimin: Entiteti i sensorit të derës, Entiteti i konfirmimit të shkarkimit, ose opsioni \"Konfirmo shkarkimin manualisht\" më poshtë. Butoni i largimit funksionon në njoftimet e aplikacionit-shoqërues (celular) të Home Assistant.",
"label": "Përsërit derisa të shkarkohet"
},
"off_delay": {
"doc": "Koha për të pritur pasi të bjerë energjia përpara se të deklaroni ciklin të përfunduar. Nëse rryma rifillon brenda kësaj dritareje, cikli vazhdon pa probleme - kjo lidh pauzat midis fazave të larjes. Enëlarëset kanë faza të gjata tharjeje (fikje për 20-60 min), kështu që vonesa e fikjes duhet ta kalojë atë për të mbajtur të gjithë larjen+tharjen si një cikël.",
@@ -1623,6 +1634,18 @@
"energy_price_dynamic": {
"label": "Kosto e ponderuar sipas kohës",
"doc": "Çdo cikël faturohet me çmimin në fuqi në çdo çast që ai punoi, në vend të një çmimi të vetëm në fuqi kur ai përfundoi. Vlen vetëm për një entitet çmimi (një çmim statik nuk ndryshon). Nuk kërkon konfigurim shtesë: çmimi ndiqet drejtpërdrejt ndërsa cikli po punon, kurse veprimi Proceso historinë te Diagnostikimi rillogarit koston e cikleve më të vjetra nga recorder. Joaktiv = sjellja klasike, një çmim i vetëm i ngrirë në fund."
},
"unload_confirm_entity": {
"label": "Entiteti i konfirmimit të shkarkimit",
"doc": "Entitet opsional, aktivizimi i të cilit do të thotë se ngarkesa është hequr, për makinat që nuk mund të kenë sensor dere. Funksionon çdo lloj: një buton Zigbee ose NFC, një ndihmës input_button, një sensor lëvizjeje përpara makinës, një skenë. Çdo ndryshim i gjendjes llogaritet si konfirmim, përveç fikjes dhe kalimit në gjendje të padisponueshme. Vendosja e kësaj aktivizon gjithashtu kujtesën për shkarkim në një pajisje pa Entitetin e sensorit të derës."
},
"unload_track_without_door": {
"label": "Konfirmo shkarkimin manualisht",
"doc": "Ekzekuto kujtesën për shkarkim në një pajisje pa Entitetin e sensorit të derës dhe pa Entitetin e konfirmimit të shkarkimit, dhe konfirmo shkarkimin nga automatizimi yt: shtyp entitetin buton \"Shëno si të shkarkuar\", ose thirr shërbimin ha_washdata.mark_unloaded. Shpërfillet kur është vendosur një Entitet i sensorit të derës, i cili tashmë e jep këtë sinjal."
},
"notify_icon_color": {
"label": "Ngjyra e njoftimit",
"doc": "Ngjyrë theksuese për njoftimet e kësaj pajisjeje, që makina larëse, tharësja dhe pjatalarësja të dallohen me një shikim. iOS ngjyros ikonën dhe shiritin e progresit të Live Activity në ekranin e kyçjes dhe në Dynamic Island. Android-it i dërgohet e njëjta ngjyrë, por çfarë bën me të varet nga telefoni: disa e ngjyrosin ikonën, ndërsa shumë (veçanërisht Samsung One UI) e shpërfillin plotësisht. Të dyja e kuptojnë kodin Hex (p.sh. #03A9F4). Lëreni bosh për parazgjedhjen e platformës."
}
},
"setting_group": {
@@ -1766,6 +1789,7 @@
},
"toast": {
"catalog_refreshed": "Katalogu i komunitetit u rifreskua",
"contrib_device_changed": "U shtua në dyqanin e komunitetit, por pajisja e zgjedhur ndryshoi ndërsa formulari ishte i hapur. Zgjidheni te Cilësimet.",
"access_saved": "Kontrolli i aksesit u ruajt",
"all_wiped": "Të gjitha të dhënat u fshien",
"analysis_complete_none": "Analiza e plotë: asnjë sugjerim i ri",
@@ -1878,7 +1902,9 @@
"unlabel_done": "U hoq etiketa nga {count} cikël(e)",
"unlabel_failed": "Heqja e etiketës dështoi: {error}",
"odometer_saved": "Totali i cikleve u ruajt",
"odometer_save_failed": "Nuk u ruajt dot totali: {error}"
"odometer_save_failed": "Nuk u ruajt dot totali: {error}",
"store_pick_profile_share": "Zgjidhni një profil për ndarje",
"remove_failed": "Heqja dështoi: {error}"
},
"suggestion": {
"both_agree": "WashData rekomandon",
@@ -1978,6 +2004,7 @@
"summary": "U përjashtuan {total} cikle të zbuluar gabimisht: {parts}.",
"reason": {
"force_stopped": "të ndalur me forcë",
"user_stopped": "të ndalur manualisht",
"interrupted": "të ndërprerë",
"incomplete": "të papërfunduar",
"noise": "zhurmë",
@@ -2021,7 +2048,7 @@
"duration_weight": "Etapa 4: sa fortë ndikon marrëveshja e kohëzgjatjes në rezultatin final (parazgjedhja 0.22)",
"energy_weight": "Etapa 4: sa fortë ndikon marrëveshja e energjisë në rezultatin final (parazgjedhja 0.22)",
"profile_match_min_duration_ratio": "Etapa 1: gjatësia minimale e ciklit si pjesë e kohëzgjatjes së profilit; parazgjedhja 0.1 (10%)",
"profile_match_max_duration_ratio": "Etapa 1: gjatësia maksimale e ciklit si pjesë e kohëzgjatjes së profilit; parazgjedhja 1.5 (150%)",
"profile_match_max_duration_ratio": "Etapa 1: gjatësia maksimale e ciklit si pjesë e kohëzgjatjes së profilit; parazgjedhja 1.8 (180%)",
"keep_min_score": "Etapa 2: rezultati minimal për të qëndruar në garë; parazgjedhja 0.1 pranon përputhje të dobëta, etapat e mëvonshme zgjedhin më të mirën",
"dtw_blend": "Etapa 3: 0 = vetëm rezultati bazë, 1 = vetëm rezultati DTW, 0.5 = përzierje e barabartë (parazgjedhja)",
"dtw_ensemble_w": "Etapa 3 (modaliteti ansambël): pesë për L1-e shkallëzuar kundrejt DTW derivativ; parazgjedhja 0.7 favorizon nivelin e fuqisë",
@@ -2187,5 +2214,10 @@
"hide_guidance": "Fshih udhëzuesin",
"show_guidance": "Shfaq udhëzuesin"
}
},
"note": {
"off_delay": {
"effective": "Një cikël i përfunduar pret në të vërtetë {eff} s, jo {delay} s: Hendeku min. i fikjes ({gap} s) është më i gjatë dhe ka përparësi."
}
}
}
@@ -60,6 +60,7 @@
"fair_fit_tip": "Umerena doslednost podudaranja – neki ciklusi dodeljeni ovom profilu imaju niže ocene pouzdanosti. Označite više ciklusa ili ponovo snimite profil da biste poboljšali tačnost.",
"feedback_requested": "Zatražena povratna informacija",
"golden_cycle": "Snimljeni referentni ciklus",
"duration_outlier": "Ciklusi neobične dužine: {n}",
"improving": "↗ poboljšava se",
"manual": "(ručno izabrano)",
"needs_review": "Potreban pregled",
@@ -256,7 +257,15 @@
"hist_scan": "Potraži cikluse",
"hist_import_n": "Uvezi cikluse: {n}",
"hist_goto_cycles": "Prikaži cikluse",
"save_total_cycles": "Sačuvaj ukupan broj"
"save_total_cycles": "Sačuvaj ukupan broj",
"clear": "Obriši",
"add_brand": "Dodaj marku",
"add_appliance": "Dodaj uređaj"
},
"note": {
"off_delay": {
"effective": "Završen ciklus zapravo čeka {eff} s, a ne {delay} s: Min. razmak isklopa ({gap} s) je duži i ima prednost."
}
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +387,8 @@
"community_store": "Prodavnica zajednice",
"online_account": "Prodavnica zajednice i online funkcije",
"import_power_history": "Uvoz istorije snage",
"service_status": "Status servisa"
"service_status": "Status servisa",
"online_account_tab": "Online i zajednica"
},
"health": {
"fair": "Prihvatljiva kvaliteta profila",
@@ -519,7 +529,8 @@
"pg_tile_matched_tip": "Ciklusi ispravno pripisani poznatom programu",
"pg_time_left": "Preostalo vreme",
"pg_weak_match": "Slabo poklapanje",
"phase_name": "Naziv",
"phase_name": "Naziv faze",
"name": "Naziv",
"phase_timeline": "Vremenska osa faza",
"power": "Snaga",
"profile": "Profil",
@@ -1032,6 +1043,9 @@
"imported_readonly": "Uvezeno iz prodavnice zajednice. Prikazano za referencu i podudaranje. Ne računa se u vašu statistiku i ne može se uređivati.",
"merge_no_imports": "Uvezeni snimci ne mogu se spojiti sa stvarnim ciklusom. Poništite njihov izbor da biste ih spojili.",
"store_enable_hint": "Omogućite online funkcije u Podešavanjima da biste mogli da pregledate i uvezete referentne cikluse zajednice.",
"advisory_duration_outlier": "Ciklusi označeni kao '{name}' ({n}) previše odstupaju od njegove uobičajene dužine da bi se ikada mogli podudariti s njim (najveće odstupanje: {ratio}x). Verovatno su pogrešno označeni ili je jedno snimanje obuhvatilo dva pokretanja. Ponovo ih označite ili ih podelite kako bi prestali da iskrivljuju procenu vremena za ovaj program.",
"standby_above_stop_title": "Još uvek povlači {idle} W kada je ciklus završio",
"standby_above_stop_sub": "{n} od poslednjih {total} ciklusa. Kašnjenje isklopa počinje tek kada snaga padne ispod Praga zaustavljanja ({stop} W), pa se ciklusi završavaju sa zakašnjenjem ili ih treba zaustaviti ručno.",
"online_intro": "Delite i preuzimajte referentne cikluse sa drugim korisnicima WashData. Sve online funkcije su dobrovoljne i podrazumevano isključene.",
"store_share_intro": "Otpremite ovaj referentni ciklus kako bi ga koristili drugi sa istim uređajem. Pregleda se pre nego što postane javno dostupan.",
"online_intro_global": "Pregledajte i delite referentne snimke sa drugim korisnicima WashData i potvrđujte unose uređaja. Jedna veza važi za celu vašu WashData integraciju; marka i model uređaja se postavljaju za svaki uređaj u odeljku Osnovno. Sve online funkcije su dobrovoljne i podrazumevano isključene.",
@@ -1087,7 +1101,9 @@
"odometer_intro": "Ovaj ukupan broj samo raste. Brisanje zapisa o ciklusu ga ne menja i nastavlja da broji i preko ograničenja sačuvane istorije, tako da podsetnici za servis ostaju tačni.",
"odometer_correct_hint": "Postavite ovo ako je WashData zabeležio ciklus koji se nikada nije dogodio ili ako je uređaj već obavio cikluse pre instaliranja WashData.",
"no_reminders_set": "Još nema postavljenih podsetnika za servis.",
"toast_program_armed": "Program podešen za sledeći ciklus: {program}"
"toast_program_armed": "Program podešen za sledeći ciklus: {program}",
"toast_range_required": "Postavite ispravan raspon Od/Do",
"no_profiles_option": "(nema profila, dodajte novi)"
},
"phase_desc": {
"anti_crease": "Povremena kratka okretanja nakon završetka radi smanjenja gužvanja.",
@@ -1414,8 +1430,8 @@
"label": "Kašnjenje podsetnika za istovar"
},
"notify_unload_repeat": {
"doc": "Ponovo šalje podsetnik za istovar svakih \"Kašnjenje podsetnika za istovar\" minuta dok ne otvorite vrata ili ne dodirnete \"Prestani da podsećaš\" na obaveštenju. Zahteva Entitetu senzora vrata (kao i sam podsetnik). Dugme za odbacivanje radi na obaveštenjima mobilne aplikacije Home Assistant (companion app).",
"label": "Ponavljaj dok se vrata ne otvore"
"doc": "Ponovo šalje podsetnik za istovar svakih \"Kašnjenje podsetnika za istovar\" minuta dok se istovar ne potvrdi ili dok ne dodirnete \"Prestani da podsećaš\" na obaveštenju. Potreban je način za potvrdu istovara: Entitet senzora vrata, Entitet potvrde istovara ili opcija \"Ručno potvrdi istovar\" ispod. Dugme za odbacivanje radi na obaveštenjima mobilne aplikacije Home Assistant (companion app).",
"label": "Ponavljaj do istovara"
},
"off_delay": {
"doc": "Vreme za čekanje nakon pada struje pre nego što proglasimo ciklus završenim. Ako se napajanje nastavi unutar ovog prozora, ciklus se nastavlja bez problema - ovo premošćuje pauze između faza pranja. Mašine za pranje sudova imaju duge faze sušenja (isključenje na 20-60 min) tako da kašnjenje isključenja mora biti veće od toga da bi se celo pranje+suvo održalo kao jedan ciklus.",
@@ -1623,6 +1639,18 @@
"energy_price_dynamic": {
"label": "Vremenski ponderisan trošak",
"doc": "Naplaćuje svaki ciklus po ceni koja je važila u svakom trenutku njegovog rada, umesto po jednoj ceni koja je važila kada je završio. Odnosi se samo na entitet cene (statična cena ne može da se menja). Ne zahteva dodatno podešavanje: cena se prati uživo dok ciklus traje, a Obrada istorije u Dijagnostici ponovo obračunava starije cikluse iz recordera. Isključeno = klasično ponašanje, jedna cena zamrznuta na kraju."
},
"unload_confirm_entity": {
"label": "Entitet potvrde istovara",
"doc": "Opcioni entitet čije aktiviranje znači da je sadržaj izvađen, za mašine koje ne mogu imati senzor vrata. Radi bilo koja vrsta: Zigbee ili NFC dugme, input_button pomoćnik, senzor pokreta ispred mašine, scena. Svaka promena stanja računa se kao potvrda, osim isključivanja i prelaska u nedostupno stanje. Postavljanje ovoga ujedno omogućava podsetnik za istovar na uređaju bez Entiteta senzora vrata."
},
"unload_track_without_door": {
"label": "Ručno potvrdi istovar",
"doc": "Pokreni podsetnik za istovar na uređaju bez Entiteta senzora vrata i bez Entiteta potvrde istovara, a istovar potvrdi iz sopstvene automatizacije: pritisni entitet dugmeta \"Označi kao istovareno\" ili pozovi uslugu ha_washdata.mark_unloaded. Zanemaruje se kada je postavljen Entitet senzora vrata, koji već daje taj signal."
},
"notify_icon_color": {
"label": "Boja obaveštenja",
"doc": "Akcentna boja obaveštenja ovog uređaja, kako bi se mašina za pranje veša, mašina za sušenje i mašina za pranje sudova razlikovale na prvi pogled. iOS boji ikonu i traku napretka Live Activity na zaključanom ekranu i u Dynamic Island. Android dobija istu boju, ali šta će s njom uraditi zavisi od telefona: neki oboje ikonu, a mnogi (naročito Samsung One UI) je potpuno ignorišu. Obe platforme razumeju Hex (npr. #03A9F4). Ostavite prazno za podrazumevanu boju platforme."
}
},
"setting_group": {
@@ -1716,7 +1744,7 @@
"all_statuses": "Svi statusi",
"ambiguous": "Višeznačan",
"auto_detect": "Automatska detekcija",
"clear": "Jasno",
"clear": "Jednoznačan",
"completed": "Završeno",
"fine_tuning": "fino podešavanje…",
"force_stopped": "Prisilno zaustavljeno",
@@ -1835,6 +1863,7 @@
"summary": "Isključeno {total} pogrešno detektovanih ciklusa: {parts}.",
"reason": {
"force_stopped": "prisilno zaustavljeni",
"user_stopped": "ručno zaustavljeni",
"interrupted": "prekinuti",
"incomplete": "nepotpuni",
"noise": "šum",
@@ -1965,6 +1994,7 @@
"store_no_appliance": "Prvo postavite marku i model uređaja u Podešavanjima.",
"store_share_failed": "Deljenje nije uspelo: {error}",
"store_shared": "Podeljeno u prodavnicu zajednice, čeka pregled.",
"contrib_device_changed": "Dodato u prodavnicu zajednice, ali se izabrani uređaj promenio dok je obrazac bio otvoren. Izaberite ga u Podešavanjima.",
"appliance_added": "Uređaj dodat, čeka odobrenje",
"thanks_confirming": "Hvala na potvrdi",
"device_approved": "Odobreno od strane zajednice",
@@ -1989,7 +2019,9 @@
"unlabel_done": "Uklonjena oznaka sa {count} ciklus(a)",
"unlabel_failed": "Uklanjanje oznake nije uspelo: {error}",
"odometer_saved": "Ukupan broj ciklusa sačuvan",
"odometer_save_failed": "Nije moguće sačuvati ukupan broj: {error}"
"odometer_save_failed": "Nije moguće sačuvati ukupan broj: {error}",
"store_pick_profile_share": "Izaberite profil za deljenje",
"remove_failed": "Uklanjanje nije uspelo: {error}"
},
"trend": {
"down": "Opadajući trend",
@@ -2024,7 +2056,7 @@
"duration_weight": "Faza 4: koliko jako slaganje trajanja utiče na konačni rezultat (podrazumevano 0,22)",
"energy_weight": "Faza 4: koliko jako slaganje energije utiče na konačni rezultat (podrazumevano 0,22)",
"profile_match_min_duration_ratio": "Faza 1: minimalna dužina ciklusa kao udeo trajanja profila; podrazumevano 0,1 (10%)",
"profile_match_max_duration_ratio": "Faza 1: maksimalna dužina ciklusa kao udeo trajanja profila; podrazumevano 1,5 (150%)",
"profile_match_max_duration_ratio": "Faza 1: maksimalna dužina ciklusa kao udeo trajanja profila; podrazumevano 1,8 (180%)",
"keep_min_score": "Faza 2: minimalni rezultat za ostanak u utrci; podrazumevano 0,1 prihvata slaba podudaranja, kasniji koraci biraju najbolje",
"dtw_blend": "Faza 3: 0 = samo osnovni rezultat, 1 = samo DTW rezultat, 0,5 = jednako mešanje (podrazumevano)",
"dtw_ensemble_w": "Faza 3 (kombinovani režim): težina skaliranog L1 naspram derivatnog DTW; podrazumevano 0,7 favorizuje nivo",
@@ -60,6 +60,7 @@
"fair_fit_tip": "Måttlig matchkonsistens – vissa cykler tilldelade den här profilen har lägre konfidenspoäng. Märk fler cykler eller spela in profilen igen för att förbättra noggrannheten.",
"feedback_requested": "Feedback begärd",
"golden_cycle": "Inspelad referenscykel",
"duration_outlier": "{n} cykel/cykler med avvikande längd",
"improving": "↗ förbättras",
"manual": "(manuellt valt)",
"needs_review": "Behöver granskning",
@@ -256,7 +257,15 @@
"hist_scan": "Sök efter cykler",
"hist_import_n": "Importera {n} cykler",
"hist_goto_cycles": "Visa mig cyklerna",
"save_total_cycles": "Spara total"
"save_total_cycles": "Spara total",
"clear": "Rensa",
"add_brand": "Lägg till märke",
"add_appliance": "Lägg till apparat"
},
"note": {
"off_delay": {
"effective": "En avslutad cykel väntar i själva verket {eff} s, inte {delay} s: Min. avlucka ({gap} s) är längre och har företräde."
}
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +387,8 @@
"community_store": "Gemenskapsbutik",
"online_account": "Gemenskapsbutik och onlinefunktioner",
"import_power_history": "Importera effekthistorik",
"service_status": "Servicestatus"
"service_status": "Servicestatus",
"online_account_tab": "Online & gemenskap"
},
"health": {
"fair": "Acceptabel profilkvalitet",
@@ -523,7 +533,8 @@
"pg_tile_matched_tip": "Cykler som korrekt tillskrivits ett känt program",
"pg_time_left": "Tid kvar",
"pg_weak_match": "Svag matchning",
"phase_name": "Namn",
"phase_name": "Fasnamn",
"name": "Namn",
"phase_timeline": "Fastidslinje",
"power": "Effekt",
"profile": "Profil",
@@ -1027,6 +1038,9 @@
"imported_readonly": "Importerad från gemenskapsbutiken. Visas för referens och matchning. Räknas inte in i din statistik och kan inte redigeras.",
"merge_no_imports": "Importerade inspelningar kan inte slås samman med en riktig cykel. Avmarkera dem för att slå samman.",
"store_enable_hint": "Aktivera onlinefunktioner i Inställningar för att bläddra bland och importera gemenskapens referenscykler.",
"advisory_duration_outlier": "{n} cykel/cykler märkta '{name}' ligger för långt från programmets vanliga längd för att någonsin kunna matcha det (värst: {ratio}x). De är troligen felmärkta, eller så fångade en inspelning två körningar. Märk om dem, eller dela upp dem, så att de slutar snedvrida tidsuppskattningen för det här programmet.",
"standby_above_stop_title": "Drog fortfarande {idle} W när cykeln avslutades",
"standby_above_stop_sub": "{n} av de senaste {total} cyklerna. Avfördröjningen startar först när effekten är under Stopptröskel ({stop} W), så cykler avslutas för sent eller måste stoppas för hand.",
"online_intro": "Dela referenscykler med andra WashData-användare och ladda ner dem. Alla onlinefunktioner är frivilliga och avstängda som standard.",
"store_share_intro": "Ladda upp den här referenscykeln så att andra med samma apparat kan använda den. Den granskas innan den visas offentligt.",
"online_intro_global": "Bläddra bland och dela referensinspelningar med andra WashData-användare, och bekräfta apparatposter. En anslutning gäller för hela din WashData-integration; apparatens märke och modell anges per enhet under Grundläggande. Alla onlinefunktioner är frivilliga och avstängda som standard.",
@@ -1087,7 +1101,9 @@
"odometer_intro": "Den här totalsumman kan bara öka. Att ta bort en cykelpost ändrar den inte, och den fortsätter räkna även utöver gränsen för sparad historik, så att servicepåminnelserna fortsätter stämma.",
"odometer_correct_hint": "Ange detta om WashData har registrerat en körning som aldrig ägde rum, eller om apparaten redan hade kört cykler innan WashData installerades.",
"no_reminders_set": "Inga servicepåminnelser är inställda än.",
"toast_program_armed": "Program valt för nästa cykel: {program}"
"toast_program_armed": "Program valt för nästa cykel: {program}",
"toast_range_required": "Ange ett giltigt Från/Till-intervall.",
"no_profiles_option": "(inga profiler ännu, lägg till en)"
},
"pg_desc": {
"dishwasher_end_spike_quiet_release": "Diskmaskin: tysta sekunder efter förväntad varaktighet innan väntan på tömning vid cykelslut frisläpps",
@@ -1105,7 +1121,7 @@
"duration_weight": "Steg 4: hur starkt körtidsöverensstämmelse påverkar slutpoängen (standard 0,22)",
"energy_weight": "Steg 4: hur starkt energiöverensstämmelse påverkar slutpoängen (standard 0,22)",
"profile_match_min_duration_ratio": "Steg 1: lägsta cykellängd som andel av profilduration; standard 0,1 (10%)",
"profile_match_max_duration_ratio": "Steg 1: högsta cykellängd som andel av profilduration; standard 1,5 (150%)",
"profile_match_max_duration_ratio": "Steg 1: högsta cykellängd som andel av profilduration; standard 1,8 (180%)",
"keep_min_score": "Steg 2: lägsta poäng för att gå vidare; standard 0,1 släpper igenom svaga matchningar, senare steg väljer den bästa",
"dtw_blend": "Steg 3: 0 = bara kärnpoäng, 1 = bara DTW-poäng, 0,5 = lika blandning (standard)",
"dtw_ensemble_w": "Steg 3 (ensemble-läge): vikt på skalad L1 kontra derivativ DTW; standard 0,7 gynnar nivåmedvetenhet",
@@ -1446,8 +1462,8 @@
"label": "Lossningspåminnelsefördröjning"
},
"notify_unload_repeat": {
"doc": "Fortsätter att skicka lossningspåminnelsen igen var \"Lossningspåminnelsefördröjning\" minut tills du öppnar dörren eller trycker på \"Sluta påminna\" i aviseringen. Kräver en Dörrsensorentitet (det gör själva påminnelsen). Avfärda-knappen fungerar på aviseringar från Home Assistant companion-appen (mobil).",
"label": "Upprepa tills dörren öppnas"
"doc": "Fortsätter att skicka lossningspåminnelsen igen var \"Lossningspåminnelsefördröjning\" minut tills lossningen har bekräftats eller du trycker på \"Sluta påminna\" i aviseringen. Kräver ett sätt att bekräfta lossningen: en Dörrsensorentitet, en Lossningsbekräftelse-entitet eller alternativet \"Bekräfta lossning manuellt\" nedan. Avfärda-knappen fungerar på aviseringar från Home Assistant companion-appen (mobil).",
"label": "Upprepa tills lossad"
},
"off_delay": {
"doc": "Dags att vänta efter strömavbrott innan man förklarar att cykeln är avslutad. Om strömmen återupptas inom detta fönster fortsätter cykeln sömlöst - detta överbryggar pauser mellan tvättstegen. Diskmaskiner har långa torkningsfaser (ström av i 20-60 min) så avstängningsfördröjningen måste överstiga den för att hålla hela tvätten+torr som en cykel.",
@@ -1655,6 +1671,18 @@
"energy_price_dynamic": {
"label": "Tidsviktad kostnad",
"doc": "Debitera varje cykel till det pris som gällde i varje ögonblick den kördes, i stället för det enda pris som gällde när den var klar. Gäller bara en prisentitet (ett statiskt pris kan inte röra sig). Kräver ingen extra inställning: priset följs live medan cykeln kör, och \"Bearbeta historik\" under Diagnostik räknar om kostnaden för äldre cykler från recordern. Av = det klassiska beteendet, ett pris fryst vid slutet."
},
"unload_confirm_entity": {
"label": "Lossningsbekräftelse-entitet",
"doc": "Valfri entitet vars aktivering betyder att tvätten har tagits ut, för maskiner som inte kan ha en dörrsensor. Alla typer fungerar: en Zigbee- eller NFC-knapp, en input_button-hjälpare, en rörelsesensor framför maskinen, en scen. Varje tillståndsändring räknas som en bekräftelse, utom att stängas av och att bli otillgänglig. Att ställa in den här aktiverar även lossningspåminnelsen på en enhet utan Dörrsensorentitet."
},
"unload_track_without_door": {
"label": "Bekräfta lossning manuellt",
"doc": "Kör lossningspåminnelsen på en enhet utan Dörrsensorentitet och utan Lossningsbekräftelse-entitet, och bekräfta lossningen från din egen automation i stället: tryck på knappentiteten \"Markera som lossad\", eller anropa åtgärden ha_washdata.mark_unloaded. Ignoreras när en Dörrsensorentitet är angiven, eftersom den redan ger signalen."
},
"notify_icon_color": {
"label": "Meddelandefärg",
"doc": "Accentfärg för den här apparatens aviseringar, så att en tvättmaskin, en torktumlare och en diskmaskin går att skilja åt med en blick. iOS färgar ikonen och Liveaktivitetens förloppsindikator på den låsta skärmen och i Dynamic Island. Android får samma färg skickad till sig, men vad telefonen gör med den varierar: vissa färgar ikonen, och många (särskilt Samsung One UI) struntar helt i den. Båda förstår hex-värden (t.ex. #03A9F4). Lämna tomt för plattformens standard."
}
},
"setting_group": {
@@ -1748,7 +1776,7 @@
"all_statuses": "Alla statusar",
"ambiguous": "Tvetydig",
"auto_detect": "Autoidentifiera",
"clear": "Tydlig",
"clear": "Entydig",
"completed": "Klar",
"fine_tuning": "finjusterar nu…",
"force_stopped": "Tvångsstoppad",
@@ -1867,6 +1895,7 @@
"summary": "Uteslöt {total} feldetekterade cykler: {parts}.",
"reason": {
"force_stopped": "tvångsstoppad",
"user_stopped": "stoppad manuellt",
"interrupted": "avbruten",
"incomplete": "ofullständig",
"noise": "brus",
@@ -2021,7 +2050,10 @@
"unlabel_done": "Märkningen togs bort från {count} cykler",
"unlabel_failed": "Borttagning av märkning misslyckades: {error}",
"odometer_saved": "Totalt antal cykler sparat",
"odometer_save_failed": "Kunde inte spara totalen: {error}"
"odometer_save_failed": "Kunde inte spara totalen: {error}",
"contrib_device_changed": "Tillagd i gemenskapsbutiken, men apparaten byttes medan formuläret var öppet. Välj den i Inställningar.",
"store_pick_profile_share": "Välj en profil att dela till",
"remove_failed": "Borttagning misslyckades: {error}"
},
"trend": {
"down": "Nedåtgående trend",
@@ -60,6 +60,7 @@
"fair_fit_tip": "Orta düzeyde eşleşme tutarlılığı – bu profile atanan bazı döngülerin güven puanları daha düşüktür. Doğruluğu artırmak için daha fazla döngü etiketleyin veya profili yeniden kaydedin.",
"feedback_requested": "Geri bildirim istendi",
"golden_cycle": "Kaydedilen referans döngüsü",
"duration_outlier": "{n} olağandışı uzunlukta döngü",
"improving": "↗ iyileşiyor",
"manual": "(manuel seçildi)",
"needs_review": "İncelenmesi gerekiyor",
@@ -256,7 +257,15 @@
"hist_scan": "Döngüleri tara",
"hist_import_n": "{n} döngüyü içe aktar",
"hist_goto_cycles": "Döngüleri göster",
"save_total_cycles": "Toplamı Kaydet"
"save_total_cycles": "Toplamı Kaydet",
"clear": "Temizle",
"add_brand": "Marka ekle",
"add_appliance": "Cihaz ekle"
},
"note": {
"off_delay": {
"effective": "Biten bir döngü gerçekte {delay} s değil {eff} s bekler: Min. Kapalı Aralık ({gap} s) daha uzun ve öncelik onundur."
}
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +387,8 @@
"community_store": "Topluluk Mağazası",
"online_account": "Topluluk Mağazası ve çevrimiçi özellikler",
"import_power_history": "Güç geçmişini içe aktar",
"service_status": "Bakım Durumu"
"service_status": "Bakım Durumu",
"online_account_tab": "Çevrimiçi ve Topluluk"
},
"health": {
"fair": "Kabul edilebilir profil kalitesi",
@@ -518,7 +528,8 @@
"pg_tile_matched_tip": "Bilinen bir programa doğru şekilde atfedilen döngüler",
"pg_time_left": "Kalan süre",
"pg_weak_match": "Zayıf eşleşme",
"phase_name": "Ad",
"phase_name": "Aşama Adı",
"name": "Ad",
"phase_timeline": "Aşama zaman çizelgesi",
"power": "Güç",
"profile": "Profil",
@@ -1027,6 +1038,9 @@
"imported_readonly": "Topluluk mağazasından içe aktarıldı. Referans ve eşleştirme için gösterilir. İstatistiklerinize sayılmaz ve düzenlenemez.",
"merge_no_imports": "İçe aktarılan kayıtlar gerçek bir döngüye birleştirilemez. Birleştirmek için seçimlerini kaldırın.",
"store_enable_hint": "Topluluk referans döngülerine göz atmak ve içe aktarmak için Ayarlar'da çevrimiçi özellikleri etkinleştirin.",
"advisory_duration_outlier": "'{name}' olarak etiketlenen {n} çevrimin süresi, bu programın olağan uzunluğuna hiç eşleşemeyecek kadar uzak (en kötüsü: {ratio}x). Muhtemelen yanlış etiketlenmişlerdir ya da tek bir kayıt iki çalışmayı birden içeriyordur. Bu programın süre tahminini bozmamaları için onları yeniden etiketleyin veya bölün.",
"standby_above_stop_title": "Döngü bittiğinde hâlâ {idle} W çekiyordu",
"standby_above_stop_sub": "Son {total} döngünün {n} tanesi. Kapatma Gecikmesi ancak güç Durdurma Eşiğinin ({stop} W) altına indiğinde başlar, bu yüzden döngüler geç biter veya elle durdurulmaları gerekir.",
"online_intro": "Diğer WashData kullanıcılarıyla referans döngüleri paylaşın ve indirin. Tüm çevrimiçi özellikler isteğe bağlıdır ve varsayılan olarak kapalıdır.",
"store_share_intro": "Aynı cihaza sahip diğerlerinin kullanabilmesi için bu referans döngüyü yükleyin. Herkese açık olmadan önce incelenir.",
"online_intro_global": "Diğer WashData kullanıcılarıyla referans kayıtlarına göz atın ve paylaşın, cihaz girişlerini onaylayın. Tek bir bağlantı tüm WashData entegrasyonunuz için geçerlidir; cihaz markası ve modeli her cihaz için Temel bölümünde ayarlanır. Tüm çevrimiçi özellikler isteğe bağlıdır ve varsayılan olarak kapalıdır.",
@@ -1087,7 +1101,9 @@
"odometer_intro": "Bu toplam yalnızca artar. Bir döngü kaydını silmek onu değiştirmez ve saklanan geçmiş sınırının ötesinde saymaya devam eder, böylece servis hatırlatıcıları doğru kalır.",
"odometer_correct_hint": "WashData gerçekleşmemiş bir çalışma kaydettiyse veya cihaz WashData kurulmadan önce zaten döngü çalıştırmışsa bu değeri ayarlayın.",
"no_reminders_set": "Henüz servis hatırlatıcısı ayarlanmadı.",
"toast_program_armed": "Program sonraki döngü için ayarlandı: {program}"
"toast_program_armed": "Program sonraki döngü için ayarlandı: {program}",
"toast_range_required": "Geçerli bir Başlangıç/Bitiş aralığı belirleyin",
"no_profiles_option": "(henüz profil yok, bir tane ekleyin)"
},
"pg_desc": {
"completion_min_seconds": "Gerçek döngü sayılan en kısa çalışma",
@@ -1108,7 +1124,7 @@
"duration_weight": "Aşama 4: çalışma süresi uyumunun nihai puanı ne kadar etkilediği (varsayılan 0.22)",
"energy_weight": "Aşama 4: enerji uyumunun nihai puanı ne kadar etkilediği (varsayılan 0.22)",
"profile_match_min_duration_ratio": "Aşama 1: profil süresinin bir kesri olarak minimum döngü uzunluğu; varsayılan 0.1 (%10)",
"profile_match_max_duration_ratio": "Aşama 1: profil süresinin bir kesri olarak maksimum döngü uzunluğu; varsayılan 1.5 (%150)",
"profile_match_max_duration_ratio": "Aşama 1: profil süresinin bir kesri olarak maksimum döngü uzunluğu; varsayılan 1.8 (%180)",
"keep_min_score": "Aşama 2: yarışta kalmak için taban puan; varsayılan 0.1 zayıf eşleşmelere izin verir, sonraki aşamalar en iyisini seçer",
"dtw_blend": "Aşama 3: 0 = yalnızca çekirdek puan, 1 = yalnızca DTW puanı, 0.5 = eşit karışım (varsayılan)",
"dtw_ensemble_w": "Aşama 3 (topluluk modu): ölçeklendirilmiş-L1 ile türev DTW arasındaki ağırlık; varsayılan 0.7 düzey farkındalığını tercih eder",
@@ -1434,8 +1450,8 @@
"label": "Boşaltma Hatırlatıcısı Gecikmesi"
},
"notify_unload_repeat": {
"doc": "Kapıyı açana veya bildirimdeki \"Hatırlatmayı durdur\" düğmesine dokunana kadar boşaltma hatırlatıcısını \"Boşaltma Hatırlatıcısı Gecikmesi\" dakikada bir yeniden gönderir. Bir Kapı Sensörü Varlığı gerektirir (hatırlatıcının kendisi de gerektirir). Kapatma düğmesi Home Assistant refakatçi uygulaması (mobil) bildirimlerinde çalışır.",
"label": "Kapı Açılana Kadar Tekrarla"
"doc": "Boşaltma onaylanana veya bildirimdeki \"Hatırlatmayı durdur\" düğmesine dokunana kadar boşaltma hatırlatıcısını \"Boşaltma Hatırlatıcısı Gecikmesi\" dakikada bir yeniden gönderir. Boşaltmayı onaylamak için bir yola ihtiyaç duyar: bir Kapı Sensörü Varlığı, bir Boşaltma Onay Varlığı veya aşağıdaki \"Boşaltmayı Elle Onayla\" seçeneği. Kapatma düğmesi Home Assistant refakatçi uygulaması (mobil) bildirimlerinde çalışır.",
"label": "Boşaltılana Kadar Tekrarla"
},
"off_delay": {
"doc": "Güç kesildikten sonra döngünün bittiğini bildirmeden önce beklenecek süre. Bu pencerede güç yeniden gelirse döngü sorunsuz bir şekilde devam eder; bu, yıkama aşamaları arasındaki duraklamaları köprüler. Bulaşık makinelerinin kurutma aşamaları uzundur (20-60 dakika boyunca güç kapalıdır), bu nedenle, tüm yıkama+kuruluğun tek bir döngüde tutulması için kapatma gecikmesinin bunu aşması gerekir.",
@@ -1655,6 +1671,18 @@
"curve_preroll_seconds": {
"label": "Eğri Ön Kaydı",
"doc": "WashData'nın, iptal edilen başlatma denemelerinden gelen kaç saniyelik okumayı bir döngünün eğrisinin başına taşıyabileceği. Bir döngünün eğrisi normalde sonunda başarılı olan başlatma denemesiyle başlar; bu nedenle yerleşmeden önce birkaç kez yoklayan makinelerde (program seçimi, kapak kilidi, ilk su alma) gerçek etkinliğin ilk dakikaları her eğride eksik kalır. Bu değeri, makinenizdeki en uzun hazırlık aşamasının biraz üzerine ayarlayın (çoğu makine için 300 sn fazlasıyla yeter). 0 kapatır. Açmanın kaydedilen başlangıcı öne çektiğini unutmayın: değişiklikten önce ve sonra kaydedilen döngüler aynı program için farklı süreler taşır ve eski döngüler elenene kadar öğrenilen ortalamalar kayar."
},
"unload_confirm_entity": {
"label": "Boşaltma Onay Varlığı",
"doc": "Kapı sensörü takılamayan makineler için, etkinleşmesi yükün çıkarıldığı anlamına gelen isteğe bağlı varlık. Her türü çalışır: bir Zigbee veya NFC düğmesi, bir input_button yardımcısı, makinenin önündeki bir hareket sensörü, bir sahne. Kapanma ve kullanılamaz duruma geçme dışındaki her durum değişikliği onay sayılır. Bunu ayarlamak, Kapı Sensörü Varlığı olmayan bir cihazda da boşaltma hatırlatıcısını etkinleştirir."
},
"unload_track_without_door": {
"label": "Boşaltmayı Elle Onayla",
"doc": "Kapı Sensörü Varlığı ve Boşaltma Onay Varlığı bulunmayan bir cihazda boşaltma hatırlatıcısını çalıştırır ve boşaltmayı bunun yerine kendi otomasyonunuzdan onaylarsınız: \"Boşaltıldı Olarak İşaretle\" düğme varlığına basın veya ha_washdata.mark_unloaded servisini çağırın. Bir Kapı Sensörü Varlığı ayarlandığında yok sayılır, çünkü sinyali zaten o sağlar."
},
"notify_icon_color": {
"label": "Bildirim Rengi",
"doc": "Bu cihazın bildirimleri için vurgu rengi; böylece çamaşır makinesi, kurutma makinesi ve bulaşık makinesi bir bakışta ayırt edilebilir. iOS simgeyi ve Canlı Etkinlik ilerleme çubuğunu Kilitli ekranda ve Dynamic Island'da renklendirir. Android'e de aynı renk gönderilir, ancak bununla ne yapacağı telefona bağlıdır: bazıları simgeyi renklendirir, birçoğu ise (özellikle Samsung One UI) rengi tamamen yok sayar. Her ikisi de Hex kodunu (ör. #03A9F4) anlar. Platform varsayılanı için boş bırakın."
}
},
"setting_group": {
@@ -1748,7 +1776,7 @@
"all_statuses": "Tüm durumlar",
"ambiguous": "Belirsiz",
"auto_detect": "Otomatik algıla",
"clear": "Temiz",
"clear": "Net",
"completed": "Tamamlandı",
"fine_tuning": "ince ayar yapılıyor…",
"force_stopped": "Zorla durduruldu",
@@ -1798,6 +1826,7 @@
},
"toast": {
"catalog_refreshed": "Topluluk kataloğu yenilendi",
"contrib_device_changed": "Topluluk mağazasına eklendi, ancak form açıkken seçili cihaz değişti. Bunu Ayarlar'dan seçin.",
"access_saved": "Erişim denetimi kaydedildi",
"all_wiped": "Tüm veriler silindi",
"analysis_complete_none": "Analiz tamamlandı: yeni öneri yok",
@@ -1910,7 +1939,9 @@
"unlabel_done": "{count} döngünün etiketi kaldırıldı",
"unlabel_failed": "Etiket kaldırma başarısız: {error}",
"odometer_saved": "Toplam döngü sayısı kaydedildi",
"odometer_save_failed": "Toplam kaydedilemedi: {error}"
"odometer_save_failed": "Toplam kaydedilemedi: {error}",
"store_pick_profile_share": "Paylaşılacak bir profil seçin",
"remove_failed": "Kaldırma başarısız: {error}"
},
"suggestion": {
"both_agree": "WashData öneriyor",
@@ -2010,6 +2041,7 @@
"summary": "{total} yanlış algılanan döngü hariç tutuldu: {parts}.",
"reason": {
"force_stopped": "zorla durdurulmuş",
"user_stopped": "elle durdurulmuş",
"interrupted": "kesintiye uğramış",
"incomplete": "tamamlanmamış",
"noise": "gürültü",
@@ -60,6 +60,7 @@
"fair_fit_tip": "Помірна постійність збігів – деякі цикли, призначені цьому профілю, мають нижчі оцінки надійності. Позначте більше циклів або перезапишіть профіль, щоб підвищити точність.",
"feedback_requested": "Запит на відгук",
"golden_cycle": "Записаний контрольний цикл",
"duration_outlier": "Цикли незвичної тривалості: {n}",
"improving": "↗ покращується",
"manual": "(вибрано вручну)",
"needs_review": "Потребує перегляду",
@@ -256,7 +257,15 @@
"hist_scan": "Знайти цикли",
"hist_import_n": "Імпортувати цикли ({n})",
"hist_goto_cycles": "Показати цикли",
"save_total_cycles": "Зберегти загальну кількість"
"save_total_cycles": "Зберегти загальну кількість",
"clear": "Очистити",
"add_brand": "Додати бренд",
"add_appliance": "Додати пристрій"
},
"note": {
"off_delay": {
"effective": "Завершений цикл насправді чекає {eff} с, а не {delay} с: Мін. пауза вимкнення ({gap} с) довша й має пріоритет."
}
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +387,8 @@
"community_store": "Магазин спільноти",
"online_account": "Магазин спільноти та онлайн-функції",
"import_power_history": "Імпорт історії потужності",
"service_status": "Стан обслуговування"
"service_status": "Стан обслуговування",
"online_account_tab": "Онлайн та спільнота"
},
"health": {
"fair": "Прийнятна якість профілю",
@@ -519,7 +529,8 @@
"pg_tile_matched_tip": "Цикли, правильно віднесені до відомої програми",
"pg_time_left": "Залишилось часу",
"pg_weak_match": "Слабкий збіг",
"phase_name": "Назва",
"phase_name": "Назва фази",
"name": "Назва",
"phase_timeline": "Хронологія фаз",
"power": "Потужність",
"profile": "Профіль",
@@ -1032,6 +1043,9 @@
"imported_readonly": "Імпортовано з магазину спільноти. Показується для довідки та зіставлення. Не враховується у вашій статистиці й не підлягає редагуванню.",
"merge_no_imports": "Імпортовані записи не можна об'єднати з реальним циклом. Зніміть їх позначку, щоб об'єднати.",
"store_enable_hint": "Увімкніть онлайн-функції в налаштуваннях, щоб переглядати та імпортувати еталонні цикли спільноти.",
"advisory_duration_outlier": "Цикли з міткою '{name}' ({n}) надто далекі від її звичайної тривалості, щоб коли-небудь із нею збігтися (найбільше відхилення: {ratio}x). Імовірно, їх позначено помилково або один запис охопив два запуски. Змініть мітку або розділіть їх, щоб вони перестали спотворювати оцінку часу для цієї програми.",
"standby_above_stop_title": "У момент завершення циклу все ще споживається {idle} Вт",
"standby_above_stop_sub": "{n} з останніх {total} циклів. Затримка вимкнення починає відлік лише після того, як потужність опуститься нижче Порога зупинки ({stop} Вт), тому цикли завершуються із запізненням або їх доводиться зупиняти вручну.",
"online_intro": "Діліться еталонними циклами з іншими користувачами WashData і завантажуйте їх. Усі онлайн-функції вмикаються за бажанням і типово вимкнені.",
"store_share_intro": "Завантажте цей еталонний цикл, щоб інші користувачі з таким самим пристроєм могли його використати. Перед публікацією він проходить перевірку.",
"online_intro_global": "Переглядайте еталонні записи та діліться ними з іншими користувачами WashData, а також підтверджуйте записи про пристрої. Одне підключення діє для всієї вашої інтеграції WashData; бренд і модель пристрою задаються для кожного пристрою в розділі «Основне». Усі онлайн-функції вмикаються за бажанням і типово вимкнені.",
@@ -1087,7 +1101,9 @@
"odometer_intro": "Ця загальна кількість лише зростає. Видалення запису про цикл її не змінює, а підрахунок продовжується й поза межами ліміту збереженої історії, тому нагадування про обслуговування залишаються правильними.",
"odometer_correct_hint": "Задайте значення, якщо WashData записав цикл, якого не було, або якщо прилад уже виконував цикли до встановлення WashData.",
"no_reminders_set": "Нагадування про обслуговування ще не задано.",
"toast_program_armed": "Програму вибрано для наступного циклу: {program}"
"toast_program_armed": "Програму вибрано для наступного циклу: {program}",
"toast_range_required": "Задайте коректний діапазон Від/До",
"no_profiles_option": "(профілів ще немає, додайте новий)"
},
"phase_desc": {
"anti_crease": "Час від часу короткі оберти після завершення для зменшення зморшок.",
@@ -1410,8 +1426,8 @@
"label": "Затримка нагадування про вивантаження"
},
"notify_unload_repeat": {
"doc": "Продовжує надсилати нагадування про вивантаження кожні \"Затримка нагадування про вивантаження\" хвилин, доки ви не відкриєте двері або не натиснете \"Не нагадувати\" у сповіщенні. Потрібна Сутність датчика дверей (як і для самого нагадування). Кнопка відхилення працює у сповіщеннях мобільного застосунку Home Assistant (companion).",
"label": "Повторювати, доки не відкриються двері"
"doc": "Продовжувати надсилати нагадування про вивантаження кожні «Затримка нагадування про вивантаження» хвилин, доки вивантаження не буде підтверджено або доки ви не натиснете «Не нагадувати» у сповіщенні. Потрібен спосіб підтвердити вивантаження: Сутність датчика дверей, Сутність підтвердження вивантаження або параметр «Підтверджувати вивантаження вручну» нижче. Кнопка відхилення працює у сповіщеннях мобільного застосунку Home Assistant (companion).",
"label": "Повторювати, доки не вивантажено"
},
"off_delay": {
"doc": "Час очікування після падіння живлення, перш ніж оголосити цикл завершеним. Якщо живлення відновлюється протягом цього вікна, цикл продовжується безперебійно - це мости призупиняються між етапами прання. Посудомийні машини мають довгі фази сушіння (вимкнення на 20-60 хв), тому затримка вимкнення повинна перевищувати цю, щоб весь цикл миття+сушіння відбувався як один цикл.",
@@ -1623,6 +1639,18 @@
"curve_preroll_seconds": {
"doc": "Скільки секунд показань від перерваних спроб запуску WashData може перенести на початок кривої циклу. Крива циклу зазвичай починається зі спроби запуску, яка врешті вдалася, тому на машині, яка робить кілька проб, перш ніж вийти на режим - вибір програми, блокування дверцят, перший набір води, - у кожній кривій бракує перших хвилин справжньої роботи. Задайте значення трохи більше за найдовший такий розгін вашої машини (300 с із запасом вистачає більшості). 0 вимикає цю функцію. Зважте, що її увімкнення зсуває записаний початок раніше, тому цикли, записані до зміни та після неї, мають різну тривалість для однієї й тієї самої програми; вивчені середні будуть зміщуватися, доки старіші цикли не вийдуть з історії.",
"label": "Передзапис кривої"
},
"unload_confirm_entity": {
"label": "Сутність підтвердження вивантаження",
"doc": "Необов'язкова сутність, спрацювання якої означає, що вміст вийнято; для машин, на які не можна встановити датчик дверей. Підійде будь-яка: кнопка Zigbee або NFC, помічник input_button, датчик руху перед машиною, сцена. Підтвердженням вважається будь-яка зміна стану, окрім вимкнення та переходу в стан «Недоступно». Встановлення цієї сутності також вмикає нагадування про вивантаження на пристрої без Сутності датчика дверей."
},
"unload_track_without_door": {
"label": "Підтверджувати вивантаження вручну",
"doc": "Використовувати нагадування про вивантаження на пристрої без Сутності датчика дверей і без Сутності підтвердження вивантаження, підтверджуючи вивантаження власною автоматизацією: натисніть сутність-кнопку «Позначити як вивантажене» або викличте службу ha_washdata.mark_unloaded. Ігнорується, якщо задано Сутність датчика дверей, яка вже дає такий сигнал."
},
"notify_icon_color": {
"label": "Колір сповіщення",
"doc": "Акцентний колір сповіщень цього приладу, щоб пральну, сушильну та посудомийну машину можна було розрізнити з першого погляду. iOS забарвлює піктограму та смужку перебігу Live Activity на екрані блокування і в Dynamic Island. Android отримує той самий колір, але що з ним робити, вирішує сам телефон: деякі забарвлюють піктограму, а багато яких (особливо Samsung One UI) повністю його ігнорують. Обидві платформи розуміють Hex (наприклад, #03A9F4). Залиште порожнім, щоб використовувати колір за замовчуванням для платформи."
}
},
"setting_group": {
@@ -1716,7 +1744,7 @@
"all_statuses": "Усі стани",
"ambiguous": "Неоднозначний",
"auto_detect": "Автовизначення",
"clear": "Чисто",
"clear": "Однозначний",
"completed": "Завершено",
"fine_tuning": "налаштування…",
"force_stopped": "Примусово зупинено",
@@ -1854,6 +1882,7 @@
"store_no_appliance": "Спочатку вкажіть бренд і модель пристрою в налаштуваннях.",
"store_share_failed": "Не вдалося поділитися: {error}",
"store_shared": "Опубліковано в магазині спільноти, очікує перевірки.",
"contrib_device_changed": "Додано до магазину спільноти, але вибраний пристрій змінився, поки форма була відкрита. Виберіть його в налаштуваннях.",
"appliance_added": "Пристрій додано, очікує схвалення",
"thanks_confirming": "Дякуємо за підтвердження",
"device_approved": "Схвалено спільнотою",
@@ -1878,7 +1907,9 @@
"unlabel_done": "Знято мітку з циклів: {count}",
"unlabel_failed": "Не вдалося зняти мітку: {error}",
"odometer_saved": "Загальну кількість циклів збережено",
"odometer_save_failed": "Не вдалося зберегти загальну кількість: {error}"
"odometer_save_failed": "Не вдалося зберегти загальну кількість: {error}",
"store_pick_profile_share": "Виберіть профіль для публікації",
"remove_failed": "Не вдалося видалити: {error}"
},
"suggestion": {
"both_agree": "WashData рекомендує",
@@ -1978,6 +2009,7 @@
"summary": "Виключено {total} помилково виявлених циклів: {parts}.",
"reason": {
"force_stopped": "примусово зупинені",
"user_stopped": "зупинені вручну",
"interrupted": "перервані",
"incomplete": "незавершені",
"noise": "шум",
@@ -2024,7 +2056,7 @@
"duration_weight": "Етап 4: наскільки сильно збіг за тривалістю впливає на кінцеву оцінку (за замовчуванням 0,22)",
"energy_weight": "Етап 4: наскільки сильно збіг за енергією впливає на кінцеву оцінку (за замовчуванням 0,22)",
"profile_match_min_duration_ratio": "Етап 1: мінімальна довжина циклу як частка тривалості профілю; за замовчуванням 0,1 (10%)",
"profile_match_max_duration_ratio": "Етап 1: максимальна довжина циклу як частка тривалості профілю; за замовчуванням 1,5 (150%)",
"profile_match_max_duration_ratio": "Етап 1: максимальна довжина циклу як частка тривалості профілю; за замовчуванням 1,8 (180%)",
"keep_min_score": "Етап 2: мінімальна оцінка для участі у змаганні; за замовчуванням 0,1 допускає слабкі збіги, пізніші етапи обирають найкращий",
"dtw_blend": "Етап 3: 0 = лише базова оцінка, 1 = лише оцінка DTW, 0,5 = рівне змішування (за замовчуванням)",
"dtw_ensemble_w": "Етап 3 (режим ансамблю): вага масштабованого L1 проти похідного DTW; за замовчуванням 0,7 надає перевагу рівню",
@@ -60,6 +60,7 @@
"fair_fit_tip": "匹配一致性适中 – 分配给此配置文件的部分周期置信度较低。标记更多周期或重新录制配置文件以提高准确性。",
"feedback_requested": "已请求反馈",
"golden_cycle": "记录参考周期",
"duration_outlier": "{n} 个时长异常的周期",
"improving": "↗ 提升中",
"manual": "(手动选择)",
"needs_review": "需要审查",
@@ -256,7 +257,15 @@
"hist_scan": "扫描周期",
"hist_import_n": "导入 {n} 个周期",
"hist_goto_cycles": "查看这些周期",
"save_total_cycles": "保存累计值"
"save_total_cycles": "保存累计值",
"clear": "清除",
"add_brand": "添加品牌",
"add_appliance": "添加家电"
},
"note": {
"off_delay": {
"effective": "已结束的周期实际等待 {eff} 秒,而不是 {delay} 秒:最小关闭间隔({gap} 秒)更长,优先生效。"
}
},
"conflict": {
"anti_wrinkle_exit": {
@@ -378,7 +387,8 @@
"community_store": "社区商店",
"online_account": "社区商店与在线功能",
"import_power_history": "导入功率历史",
"service_status": "保养状态"
"service_status": "保养状态",
"online_account_tab": "在线与社区"
},
"health": {
"fair": "配置文件质量尚可",
@@ -518,7 +528,8 @@
"pg_tile_matched_tip": "正确归属到已知程序的周期",
"pg_time_left": "剩余时间",
"pg_weak_match": "弱匹配",
"phase_name": "名称",
"phase_name": "阶段名称",
"name": "名称",
"phase_timeline": "阶段时间线",
"power": "功率",
"profile": "配置文件",
@@ -1027,6 +1038,9 @@
"imported_readonly": "从社区商店导入。用于参考和匹配。不计入您的统计,且无法编辑。",
"merge_no_imports": "导入的录制无法合并到真实周期中。取消选择它们以进行合并。",
"store_enable_hint": "在设置中启用在线功能,以浏览和导入社区参考周期。",
"advisory_duration_outlier": "标记为 '{name}' 的 {n} 个周期与该程序的常见时长相差太远,永远无法与之匹配(最差:{ratio}x)。它们可能被标记错了,或者一次录制包含了两次运行。请重新标记或拆分它们,以免继续扭曲该程序的时间估算。",
"standby_above_stop_title": "周期结束时仍在消耗 {idle} W",
"standby_above_stop_sub": "最近 {total} 个周期中有 {n} 个。关闭延迟只有在功率低于停止阈值({stop} W)之后才开始计时,因此周期会结束过晚或需要手动停止。",
"online_intro": "与其他 WashData 用户分享和下载参考周期。所有在线功能均为自愿加入,默认关闭。",
"store_share_intro": "上传此参考周期,以便拥有相同家电的其他人使用。它会经过审核后才公开显示。",
"online_intro_global": "与其他 WashData 用户浏览和分享参考录制,并确认家电条目。一次连接适用于您的整个 WashData 集成;家电品牌和型号在“基本配置”下按设备设置。所有在线功能均为自愿加入,默认关闭。",
@@ -1087,7 +1101,9 @@
"odometer_intro": "此累计值只会增加。删除某条周期记录不会改变它,并且它会在保存的历史记录上限之外继续累加,因此保养提醒始终准确。",
"odometer_correct_hint": "如果 WashData 记录了从未发生的运行,或者设备在安装 WashData 之前已经运行过周期,请设置此值。",
"no_reminders_set": "尚未设置保养提醒。",
"toast_program_armed": "已为下一个周期设置程序:{program}"
"toast_program_armed": "已为下一个周期设置程序:{program}",
"toast_range_required": "请设置有效的起始/结束范围。",
"no_profiles_option": "(暂无配置文件,请添加)"
},
"pg_desc": {
"completion_min_seconds": "计为真实周期的最短运行时间",
@@ -1108,7 +1124,7 @@
"duration_weight": "Stage 4: 运行时长一致性对最终得分的影响程度(默认 0.22)",
"energy_weight": "Stage 4: 能耗一致性对最终得分的影响程度(默认 0.22)",
"profile_match_min_duration_ratio": "Stage 1: 最短允许运行时长占档案时长的比例; 默认 0.1 (10%)",
"profile_match_max_duration_ratio": "Stage 1: 最长允许运行时长占档案时长的比例; 默认 1.5 (150%)",
"profile_match_max_duration_ratio": "Stage 1: 最长允许运行时长占档案时长的比例; 默认 1.8 (180%)",
"keep_min_score": "Stage 2: 保留在竞争中的最低得分; 默认 0.1 接受弱候选,后续阶段选出最佳",
"dtw_blend": "Stage 3: 0 = 仅核心得分,1 = 仅 DTW 得分,0.5 = 等权混合(默认)",
"dtw_ensemble_w": "Stage 3 (集成模式): 缩放 L1 对导数 DTW 的权重; 默认 0.7 偏重水平感知",
@@ -1434,8 +1450,8 @@
"label": "卸载提醒延迟"
},
"notify_unload_repeat": {
"doc": "每隔 \"卸载提醒延迟\" 分钟持续重新发送卸载提醒,直到您打开门或点按通知上的 \"停止提醒\"。需要门传感器实体(提醒本身即需要)。关闭按钮适用于 Home Assistant 配套应用(移动端)通知。",
"label": "重复直到门打开"
"doc": "每隔 \"卸载提醒延迟\" 分钟持续重新发送卸载提醒,直到卸载被确认或您点按通知上的 \"停止提醒\"。需要一种确认卸载的方式:门传感器实体、卸载确认实体,或下方的 \"手动确认卸载\" 选项。关闭按钮适用于 Home Assistant 配套应用(移动端)通知。",
"label": "重复直到卸载"
},
"off_delay": {
"doc": "断电后宣告周期结束之前等待的时间。如果在此窗口内恢复供电,则循环将无缝继续 - 这会在清洗阶段之间建立暂停的桥梁。洗碗机具有较长的干燥阶段(关闭电源 20-60 分钟),因此关闭延迟必须超过该时间才能保持整个洗涤+干燥为一个循环。",
@@ -1655,6 +1671,18 @@
"curve_preroll_seconds": {
"label": "曲线前导采样",
"doc": "WashData 最多可以把中止的启动尝试中多少秒的读数并入周期曲线的开头。周期曲线通常从最终成功的那次启动尝试开始,因此对于需要试探几次才稳定下来的机器(选择程序、门锁、首次进水),每条曲线都会缺少真实运行的最初几分钟。请把此值设得略大于您的机器最长的一次起步过程(多数情况下 300 秒绰绰有余)。0 表示关闭。请注意,开启后记录的开始时间会提前,因此在更改前后记录的周期,即使是同一个程序,时长也会不同;在较早的周期被淘汰之前,学习到的平均值会一直存在偏差。"
},
"unload_confirm_entity": {
"label": "卸载确认实体",
"doc": "可选实体,其触发即表示物品已被取出,适用于无法安装门传感器的机器。任何类型都可以:Zigbee 或 NFC 按钮、input_button 辅助元素、机器前方的运动传感器、场景。除了关闭和变为不可用之外,每次状态变化都算作一次确认。设置此项后,即使设备没有门传感器实体也会启用卸载提醒。"
},
"unload_track_without_door": {
"label": "手动确认卸载",
"doc": "在既没有门传感器实体也没有卸载确认实体的设备上运行卸载提醒,改为通过您自己的自动化来确认卸载:按下 \"标记为已卸载\" 按钮实体,或调用 ha_washdata.mark_unloaded 服务。已设置门传感器实体时将忽略此项,因为它已经提供了该信号。"
},
"notify_icon_color": {
"label": "通知颜色",
"doc": "此家电通知使用的强调色,让洗衣机、烘干机和洗碗机一眼就能区分开。iOS 会为图标以及锁定屏幕和灵动岛上的实时活动进度条着色。Android 也会收到同样的颜色,但如何处理取决于手机:有些会为图标着色,也有不少(尤其是 Samsung One UI)完全忽略。十六进制(例如 #03A9F4)两者都支持。留空则使用平台默认值。"
}
},
"setting_group": {
@@ -1748,7 +1776,7 @@
"all_statuses": "所有状态",
"ambiguous": "不明确",
"auto_detect": "自动检测",
"clear": "正常",
"clear": "明确",
"completed": "已完成",
"fine_tuning": "正在微调…",
"force_stopped": "强制停止",
@@ -1910,7 +1938,10 @@
"unlabel_done": "已取消标记 {count} 个周期",
"unlabel_failed": "取消标记失败:{error}",
"odometer_saved": "已保存累计周期数",
"odometer_save_failed": "无法保存累计值:{error}"
"odometer_save_failed": "无法保存累计值:{error}",
"contrib_device_changed": "已添加到社区商店,但表单打开期间所选设备已更改。请在设置中选择。",
"store_pick_profile_share": "请选择要分享到的配置文件",
"remove_failed": "移除失败:{error}"
},
"suggestion": {
"both_agree": "WashData建议",
@@ -2010,6 +2041,7 @@
"summary": "已排除 {total} 个误检测周期:{parts}。",
"reason": {
"force_stopped": "强制停止",
"user_stopped": "手动停止",
"interrupted": "中断",
"incomplete": "未完成",
"noise": "噪声",
@@ -280,6 +280,16 @@
"description": "Urządzenie WashData, dla którego mają być przetrenowane modele."
}
}
},
"mark_unloaded": {
"name": "Oznacz jako rozładowane",
"description": "Potwierdź, że gotowy wsad został wyjęty z urządzenia WashData, co kasuje stan „Czysty” i ewentualne przypomnienie o rozładunku.",
"fields": {
"device_id": {
"name": "Urządzenie",
"description": "Urządzenie WashData, z którego wyjęto gotowy wsad."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Zatrzymaj nagrywanie cyklu"
},
"mark_unloaded": {
"name": "Oznacz jako rozładowane"
}
}
},
@@ -280,6 +280,16 @@
"description": "The WashData device to retrain models for."
}
}
},
"mark_unloaded": {
"name": "Marcar como descarregado",
"description": "Confirme que a carga finalizada foi retirada de um dispositivo WashData, limpando o estado Limpo e qualquer lembrete de descarga.",
"fields": {
"device_id": {
"name": "Dispositivo",
"description": "O dispositivo WashData cuja carga finalizada foi retirada."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Parar gravação do ciclo"
},
"mark_unloaded": {
"name": "Marcar como descarregado"
}
}
},
@@ -280,6 +280,16 @@
"description": "O dispositivo WashData para retreinar modelos."
}
}
},
"mark_unloaded": {
"name": "Marcar como descarregado",
"description": "Confirme que a carga terminada foi retirada de um dispositivo WashData, limpando o estado Limpo e qualquer lembrete de descarga.",
"fields": {
"device_id": {
"name": "Dispositivo",
"description": "O dispositivo WashData cuja carga terminada foi retirada."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Parar gravação do ciclo"
},
"mark_unloaded": {
"name": "Marcar como descarregado"
}
}
},
@@ -280,6 +280,16 @@
"description": "The WashData device to retrain models for."
}
}
},
"mark_unloaded": {
"name": "Marcați ca descărcat",
"description": "Confirmați că încărcătura terminată a fost scoasă dintr-un dispozitiv WashData, ștergând starea Curat și orice memento de descărcare.",
"fields": {
"device_id": {
"name": "Dispozitiv",
"description": "Dispozitivul WashData din care a fost scoasă încărcătura terminată."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Opriți înregistrarea ciclului"
},
"mark_unloaded": {
"name": "Marcați ca descărcat"
}
}
},
@@ -280,6 +280,16 @@
"description": "The WashData device to retrain models for."
}
}
},
"mark_unloaded": {
"name": "Отметить как выгруженное",
"description": "Подтвердите, что содержимое завершённого цикла извлечено из устройства WashData: состояние «Чистый» и напоминание о выгрузке будут сброшены.",
"fields": {
"device_id": {
"name": "Устройство",
"description": "Устройство WashData, из которого извлечено содержимое завершённого цикла."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Остановить запись цикла"
},
"mark_unloaded": {
"name": "Отметить как выгруженное"
}
}
},
@@ -280,6 +280,16 @@
"description": "The WashData device to retrain models for."
}
}
},
"mark_unloaded": {
"name": "Označiť ako vyložené",
"description": "Potvrďte, že dokončená náplň bola vybratá zo zariadenia WashData, čím sa zruší stav „Čistý“ aj prípadná pripomienka vyloženia.",
"fields": {
"device_id": {
"name": "Zariadenie",
"description": "Zariadenie WashData, z ktorého bola vybratá dokončená náplň."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Zastaviť záznam cyklu"
},
"mark_unloaded": {
"name": "Označiť ako vyložené"
}
}
},
@@ -280,6 +280,16 @@
"description": "Naprava WashData za prekvalificiranje modelov."
}
}
},
"mark_unloaded": {
"name": "Označi kot raztovorjeno",
"description": "Potrdite, da je bila končana vsebina vzeta iz naprave WashData, s čimer se počisti stanje „čisto“ in morebitni opomnik za raztovarjanje.",
"fields": {
"device_id": {
"name": "Naprava",
"description": "Naprava WashData, iz katere je bila vzeta končana vsebina."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Ustavi snemanje cikla"
},
"mark_unloaded": {
"name": "Označi kot raztovorjeno"
}
}
},
@@ -280,6 +280,16 @@
"description": "Pajisja WashData për të ritrajnuar modelet."
}
}
},
"mark_unloaded": {
"name": "Shëno si të shkarkuar",
"description": "Konfirmo se ngarkesa e përfunduar është hequr nga një pajisje WashData, duke pastruar gjendjen \"I pastër\" dhe çdo kujtesë për shkarkim.",
"fields": {
"device_id": {
"name": "Pajisja",
"description": "Pajisja WashData nga e cila është hequr ngarkesa e përfunduar."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Ndalo regjistrimin e ciklit"
},
"mark_unloaded": {
"name": "Shëno si të shkarkuar"
}
}
},
@@ -280,6 +280,16 @@
"description": "WashData uređaj za ponovnu obuku modela."
}
}
},
"mark_unloaded": {
"name": "Označi kao istovareno",
"description": "Potvrdi da je gotov sadržaj izvađen iz WashData uređaja, čime se briše stanje \"Čisto\" i svaki podsetnik za istovar.",
"fields": {
"device_id": {
"name": "Uređaj",
"description": "Uređaj WashData iz kojeg je izvađen gotov sadržaj."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Zaustavi snimanje ciklusa"
},
"mark_unloaded": {
"name": "Označi kao istovareno"
}
}
},
@@ -280,6 +280,16 @@
"description": "WashData-enheten att omskola modeller till."
}
}
},
"mark_unloaded": {
"name": "Markera som lossad",
"description": "Bekräfta att den färdiga tvätten har tagits ut ur en WashData-enhet, vilket rensar tillståndet \"Rena\" och en eventuell lossningspåminnelse.",
"fields": {
"device_id": {
"name": "Enhet",
"description": "WashData-enheten vars färdiga tvätt har tagits ut."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Stoppa cykelinspelning"
},
"mark_unloaded": {
"name": "Markera som lossad"
}
}
},
@@ -280,6 +280,16 @@
"description": "The WashData device to retrain models for."
}
}
},
"mark_unloaded": {
"name": "Boşaltıldı Olarak İşaretle",
"description": "Bir WashData cihazındaki tamamlanmış yükün çıkarıldığını onaylar, Temiz durumunu ve varsa boşaltma hatırlatıcısını temizler.",
"fields": {
"device_id": {
"name": "Cihaz",
"description": "Tamamlanmış yükü çıkarılan WashData cihazı."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Döngü Kaydını Durdur"
},
"mark_unloaded": {
"name": "Boşaltıldı Olarak İşaretle"
}
}
},
@@ -280,6 +280,16 @@
"description": "The WashData device to retrain models for."
}
}
},
"mark_unloaded": {
"name": "Позначити як вивантажене",
"description": "Підтвердьте, що вміст завершеного циклу вийнято з пристрою WashData: стан «Чистий» і нагадування про вивантаження буде скинуто.",
"fields": {
"device_id": {
"name": "Пристрій",
"description": "Пристрій WashData, з якого вийнято вміст завершеного циклу."
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "Зупинити запис циклу"
},
"mark_unloaded": {
"name": "Позначити як вивантажене"
}
}
},
@@ -280,6 +280,16 @@
"description": "要重新训练模型的 WashData 设备。"
}
}
},
"mark_unloaded": {
"name": "标记为已卸载",
"description": "确认已从 WashData 设备中取出完成的物品,清除干净状态和所有卸载提醒。",
"fields": {
"device_id": {
"name": "设备",
"description": "已取出完成物品的 WashData 设备。"
}
}
}
},
"entity": {
@@ -388,6 +398,9 @@
},
"record_stop": {
"name": "停止记录循环"
},
"mark_unloaded": {
"name": "标记为已卸载"
}
}
},
+459 -185
View File
@@ -48,6 +48,7 @@ from .const import (
ANTI_CREASE_FINALIZE_RATIO_MAX,
CONF_CURVE_PREROLL_SECONDS,
CONF_DOOR_SENSOR_ENTITY,
CONF_UNLOAD_CONFIRM_ENTITY,
CONF_ANTI_WRINKLE_EXIT_POWER,
CONF_ANTI_WRINKLE_MAX_POWER,
CONF_DURATION_TOLERANCE,
@@ -71,6 +72,9 @@ from .const import (
CONF_PROFILE_UNMATCH_THRESHOLD,
CONF_PUMP_STUCK_DURATION,
CONF_POWER_OFF_THRESHOLD_W,
CONF_POWER_SENSOR,
CONF_ENERGY_PRICE_ENTITY,
CONF_NOTIFY_PEOPLE,
CONF_SAMPLING_INTERVAL,
CONF_SMOOTHING_WINDOW,
CONF_START_DURATION_THRESHOLD,
@@ -83,6 +87,7 @@ from .const import (
DEFAULT_SMART_TERMINATION_DURATION_RATIO,
DEFAULT_SMART_TERMINATION_DURATION_RATIO_BY_DEVICE,
DEFAULT_ANTI_CREASE_FINALIZE_RATIO,
DEFAULT_PROFILE_MATCH_MAX_DURATION_RATIO,
DEFAULT_CURVE_PREROLL_SECONDS,
CURVE_PREROLL_MAX_SECONDS,
resolve_sampling_interval_default,
@@ -93,6 +98,8 @@ from .const import (
DEFAULT_MIN_POWER,
DEFAULT_OFF_DELAY,
DEFAULT_OFF_DELAY_BY_DEVICE,
resolve_min_off_gap_default,
resolve_off_delay_default,
DEFAULT_PROFILE_MATCH_THRESHOLD,
DEVICE_TYPE_PUMP,
MAINTENANCE_EVENT_TYPES,
@@ -415,6 +422,7 @@ def _get_manager(hass: HomeAssistant, entry_id: str) -> Any | None:
# hass.data (not module-global) so each lock is created inside — and bound to —
# the running event loop, which keeps it correct across test event loops.
_WS_WRITE_LOCKS_KEY = f"{DOMAIN}_ws_write_locks"
_WS_OPTIONS_LOCKS_KEY = f"{DOMAIN}_ws_options_locks"
def _entry_write_lock(hass: HomeAssistant, entry_id: str) -> asyncio.Lock:
@@ -427,6 +435,30 @@ def _entry_write_lock(hass: HomeAssistant, entry_id: str) -> asyncio.Lock:
return lock
def _entry_options_lock(hass: HomeAssistant, entry_id: str) -> asyncio.Lock:
"""Per-entry lock for ``entry.options`` read-record-update sections only.
Deliberately NOT ``_entry_write_lock``. That one is held for the whole run
of the long detached tasks - ``_reprocess_task`` across rematching,
suggestions, ML training, recosting and health recompute;
``_ml_training_task`` across a full training run; ``_rebuild_envelopes_task``
across every profile - so putting a Settings save behind it means the save
blocks for as long as the task takes, which on a slow host is minutes with
no feedback to the user. Serialising the option writers against each other
is all the race needs.
**Lock order where both are held: write lock first, then this one.** The
import handlers are the only place that happens, and they follow it, so the
pair cannot deadlock.
"""
locks: dict[str, asyncio.Lock] = hass.data.setdefault(_WS_OPTIONS_LOCKS_KEY, {})
lock = locks.get(entry_id)
if lock is None:
lock = asyncio.Lock()
locks[entry_id] = lock
return lock
def _get_entry(hass: HomeAssistant, entry_id: str) -> Any | None:
return next(
(e for e in hass.config_entries.async_entries(DOMAIN) if e.entry_id == entry_id),
@@ -486,6 +518,33 @@ _CHANGELOG_SKIP_KEYS = frozenset({CONF_NAME})
# here; relocating them to entry.data would shadow the option-first reads.
_OPTIONS_IDENTITY_KEYS = frozenset({CONF_NAME})
# Option keys whose value names an entity or device on THIS Home Assistant.
# They are ordinary tunables when the panel writes them (the user picks from a
# selector listing their own entities), but an *imported* export carries the
# SOURCE system's ids, and applying those re-points this device at entities that
# do not exist here. ``power_sensor`` is the severe one: the integration goes
# silently dead, state stuck at "off" and current_power at 0, with nothing in the
# log but one INFO line. ``ws_import_config`` already refuses to write the
# exporter's ``entry.data`` for exactly this reason - "blindly applying it would
# hijack this device's sensor binding" - but the same keys live in entry.options
# post-3.6, so they arrived through the other door. Found by the test box
# (devtools/testbox) on its first run; see register item 317.
#
# Deliberately NOT here: the notify_*_services lists. They also name the source
# user's targets, but they are shown plainly in the panel's Notifications
# section, and carrying them is usually the point when migrating your own setup.
_IMPORT_LOCAL_BINDING_KEYS = _OPTIONS_IDENTITY_KEYS | frozenset({
CONF_POWER_SENSOR,
CONF_ENERGY_SENSOR,
CONF_ENERGY_PRICE_ENTITY,
CONF_DOOR_SENSOR_ENTITY,
CONF_UNLOAD_CONFIRM_ENTITY,
CONF_SWITCH_ENTITY,
CONF_EXTERNAL_END_TRIGGER,
CONF_LINKED_DEVICE,
CONF_NOTIFY_PEOPLE,
})
def _json_safe(value: Any) -> Any:
"""Best-effort coercion of an option value to a JSON-serializable form."""
@@ -526,6 +585,42 @@ def _diff_option_changes(
return changes
async def _record_option_changes(
hass: HomeAssistant, entry: Any, updates: dict[str, Any], source: str
) -> None:
"""Record an option write in the settings changelog (#442).
``ws_set_options`` did this inline and was the only writer that did, so every
other path that writes ``entry.options`` was invisible in the history and its
values could not be reverted per setting. The one the reporter hit is "Apply
all": nine tunables changed at once with nothing recorded, and the previous
values were only recoverable because they happened to have an older
diagnostics dump.
Must be awaited BEFORE ``async_update_entry``, which schedules a reload that
rebuilds the store. Never raises: a changelog failure must not cost the write
it is describing (same contract as the inline version).
"""
if not updates:
return
try:
old_effective = {**getattr(entry, "data", {}), **getattr(entry, "options", {})}
changes = _diff_option_changes(old_effective, updates)
if not changes:
return
manager = _get_manager(hass, entry.entry_id)
store = getattr(manager, "profile_store", None) if manager else None
if store is not None:
await store.async_record_settings_changes(changes)
except Exception as exc: # pylint: disable=broad-exception-caught
_LOGGER.debug(
"Settings changelog recording failed for %s (%s): %s",
getattr(entry, "entry_id", "?"),
source,
exc,
)
# ─── Panel config + RBAC ────────────────────────────────────────────────────────
_PANEL_STORE_VERSION = 1
@@ -1186,7 +1281,15 @@ async def ws_store_download_device(hass, connection, msg):
}
entry = _get_entry(hass, msg["entry_id"])
if filtered and entry is not None:
hass.config_entries.async_update_entry(entry, options={**entry.options, **filtered})
# Same critical section as ws_set_options: the changelog snapshot and
# the options write have to be atomic per entry, or a concurrent
# writer records the same "old" value and one of the two updates is
# silently lost (#442 follow-up).
async with _entry_options_lock(hass, msg["entry_id"]):
await _record_option_changes(hass, entry, filtered, "store_download")
hass.config_entries.async_update_entry(
entry, options={**entry.options, **filtered}
)
settings_applied = len(filtered)
res = {**res, "settings_applied": settings_applied}
manager.notify_update()
@@ -1340,6 +1443,10 @@ def ws_get_devices(
"title": entry.title,
"detector_state": "unknown",
"sub_state": None,
# Declared non-optional in DeviceInfo, so it is defaulted HERE rather
# than at each exit: a device with no loaded manager never reaches
# the probe at all, which is the case the contract test caught.
"standby_above_stop": None,
"current_program": None,
"time_remaining_s": None,
"total_duration_s": None,
@@ -1360,7 +1467,12 @@ def ws_get_devices(
# and the branch is skipped for a manager-less entry (mid-setup, stale,
# or a setup that failed) as well as short-circuited by its own except.
"envelope_position": None,
"options": dict(entry.options),
# Merged data+options, matching ``ws_get_options`` and the
# options-first resolution in WashDataManager (#450). An entry added
# after its last schema migration carries the structural keys in
# ``entry.data`` only, so serving bare options handed the panel a
# device_type fallback that did not match the running manager.
"options": {**entry.data, **entry.options, CONF_NAME: entry.title},
# Device-resolved defaults for the cadence/ratio fields (#396/#393) so the
# device-list conflict/suggestion badges score an unset field against the
# value the integration would actually use, matching the Settings tab.
@@ -1406,12 +1518,18 @@ def ws_get_devices(
store = getattr(manager, "profile_store", None)
if store is not None:
# Assigned per entry and OUTSIDE both probes. It used to be
# set inside the suggestion-badge `try`, after
# `store.get_suggestions()`: if that raised, `merged` kept
# the PREVIOUS entry's options and the standby probe below
# read another device's stop threshold - or, on the first
# entry, raised NameError into a debug-level log.
merged = {**entry.data, **entry.options}
try:
# Same filters as ws_get_suggestions (muted keys and
# no-op values dropped) so the device-pill badge can
# never disagree with the Settings tab banner.
raw = store.get_suggestions() or {}
merged = {**entry.data, **entry.options}
try:
muted = set(store.get_locked_suggestions() or [])
except Exception: # pylint: disable=broad-exception-caught
@@ -1431,6 +1549,40 @@ def ws_get_devices(
info["suggestions_count"] = len(keys)
except Exception: # pylint: disable=broad-exception-caught
pass
try:
# Imported here rather than at module scope to keep the
# ws_api import graph free of suggestion_engine, as every
# other use in this file does.
from .suggestion_engine import ( # pylint: disable=import-outside-toplevel
detect_standby_above_stop,
)
# #445 cause 1: an appliance whose standby draw sits ABOVE
# stop_threshold_w can never finish a cycle on its own,
# because the off delay only starts once power is below it.
# Surfaced as its own attention card rather than folded into
# a suggestion: no threshold value can fix the case where the
# appliance's idle and working power are the same level, so
# this explains rather than proposes.
# Resolved in steps, not as an inline `.get(key, default)`:
# Python evaluates that default eagerly, so a detector
# without a bound `config` raised AttributeError before the
# option was even consulted - and the handler below turned
# that into a silently absent advisory.
_stop_w = merged.get(CONF_STOP_THRESHOLD_W)
if _stop_w is None:
_cfg = getattr(getattr(manager, "detector", None), "config", None)
_stop_w = getattr(_cfg, "stop_threshold_w", 0.0)
info["standby_above_stop"] = detect_standby_above_stop(
store.get_past_cycles() or [],
float(_stop_w or 0.0),
)
except Exception: # pylint: disable=broad-exception-caught
# Logged, not swallowed silently: this advisory shipped dead
# for a round because a NameError here was indistinguishable
# from "no pattern found".
_LOGGER.debug("standby_above_stop probe failed", exc_info=True)
# Key already defaulted to None where `info` is built.
try:
# Count only pending feedback whose cycle still exists, so the
# badge cannot outrun the review list after a cycle is deleted,
@@ -1554,6 +1706,23 @@ def _resolved_option_defaults(device_type: str) -> dict[str, Any]:
# the individual machine), but the panel pre-populates it from the same
# payload, so it is published here rather than left to the JS default.
CONF_ANTI_CREASE_FINALIZE_RATIO: DEFAULT_ANTI_CREASE_FINALIZE_RATIO,
# Item 311 raised this to 1.8 in Python and the panel's schema literal
# stayed at 1.5, so an entry without the key rendered 1.5 while the
# matcher used 1.8. Not device-resolved either; published for the same
# reason as the line above, so the constant is the only source and the
# two cannot drift again. Round 17 removed the migration seed that had
# been hiding this for legacy entries.
CONF_PROFILE_MATCH_MAX_DURATION_RATIO: (
DEFAULT_PROFILE_MATCH_MAX_DURATION_RATIO
),
# #445: the pair that decides when a cycle is allowed to end. The detector
# waits max(off_delay, min_off_gap), so an unset min_off_gap silently
# raises a hand-lowered off_delay to the per-device prior - and with the
# key absent here the panel rendered an empty field and the cross-field
# rule's `!= null` guard short-circuited, so nothing anywhere showed the
# number that was actually governing the wait.
CONF_MIN_OFF_GAP: resolve_min_off_gap_default(device_type),
CONF_OFF_DELAY: resolve_off_delay_default(device_type),
}
@@ -1612,179 +1781,201 @@ async def ws_set_options(
if not entry:
connection.send_error(msg["id"], "not_found", f"Entry {msg['entry_id']!r} not found")
return
# Build the new options from the *existing* options plus the submitted
# values only. Never spread entry.data in: that would copy identity and
# data-only keys (name, initial_profile, stale creation-time identity) into
# options where they don't belong. Tunables (including device_type /
# power_sensor / min_power, which live in options post-3.6) are preserved
# from entry.options and overridden by the submission.
new_options = {**entry.options, **msg["options"]}
# Capture the submitted display name for the entry title before it is
# stripped out of options below.
submitted_name = new_options.get(CONF_NAME)
# Mirror the OptionsFlow save-time normalization so the panel can never
# persist stale or invalid values:
# - a cleared selector (entity / linked device / trigger) becomes None so
# the link or subscription is removed rather than left dangling;
# - pump-only keys are dropped for non-pump device types;
# - the transient "apply suggestions" flag is never stored.
for key in (
CONF_EXTERNAL_END_TRIGGER,
CONF_DOOR_SENSOR_ENTITY,
CONF_LINKED_DEVICE,
CONF_SWITCH_ENTITY,
CONF_ENERGY_SENSOR,
):
if key in new_options and not new_options[key]:
new_options[key] = None
# Resolve the effective device type option-first (submission -> existing
# options -> data -> default) so the pump-only key is dropped correctly even
# when the submission omits device_type.
effective_device_type = new_options.get(
CONF_DEVICE_TYPE, entry.data.get(CONF_DEVICE_TYPE, DEFAULT_DEVICE_TYPE)
)
if effective_device_type != DEVICE_TYPE_PUMP:
new_options.pop(CONF_PUMP_STUCK_DURATION, None)
# Numeric-finite validation for fields that the cycle-detector float()-casts at
# build time; coerce bad submissions to the compiled default so storage stays clean.
if CONF_DISHWASHER_END_SPIKE_QUIET_RELEASE in new_options:
try:
_qr = float(new_options[CONF_DISHWASHER_END_SPIKE_QUIET_RELEASE])
if not math.isfinite(_qr):
raise ValueError("non-finite")
new_options[CONF_DISHWASHER_END_SPIKE_QUIET_RELEASE] = _qr
except (TypeError, ValueError):
new_options[CONF_DISHWASHER_END_SPIKE_QUIET_RELEASE] = (
DISHWASHER_END_SPIKE_QUIET_RELEASE_SECONDS
)
# Smart-Termination duration ratio (#393): fraction of expected duration, so it
# is meaningless outside [0.50, 1.00] - clamp valid submissions to the range.
# An empty or non-numeric value drops the key so the device-type default
# (resolved in the config builder, 0.99 dishwasher / 0.98 other) applies again;
# coercing to a single scalar default here would be wrong for dishwashers.
if CONF_SMART_TERMINATION_DURATION_RATIO in new_options:
_raw_str = new_options[CONF_SMART_TERMINATION_DURATION_RATIO]
if _raw_str in (None, ""):
new_options.pop(CONF_SMART_TERMINATION_DURATION_RATIO, None)
else:
try:
_str = float(_raw_str)
if not math.isfinite(_str):
raise ValueError("non-finite")
new_options[CONF_SMART_TERMINATION_DURATION_RATIO] = min(
1.0, max(0.5, _str)
)
except (TypeError, ValueError):
new_options.pop(CONF_SMART_TERMINATION_DURATION_RATIO, None)
# #429: the anti-crease finalise ratio is the same kind of value against the
# same kind of mean, and equally meaningless outside [0.50, 1.00]. An empty or
# non-numeric submission drops the key so DEFAULT_ANTI_CREASE_FINALIZE_RATIO
# applies again.
if CONF_ANTI_CREASE_FINALIZE_RATIO in new_options:
_raw_ac = new_options[CONF_ANTI_CREASE_FINALIZE_RATIO]
if _raw_ac in (None, ""):
new_options.pop(CONF_ANTI_CREASE_FINALIZE_RATIO, None)
else:
try:
_ac = float(_raw_ac)
if not math.isfinite(_ac):
raise ValueError("non-finite")
new_options[CONF_ANTI_CREASE_FINALIZE_RATIO] = min(
ANTI_CREASE_FINALIZE_RATIO_MAX,
max(ANTI_CREASE_FINALIZE_RATIO_MIN, _ac),
)
# OverflowError too (register item 194): json parses an integer literal
# of any length into an unbounded int, and float() on one of those raises
# rather than returning inf. Uncaught it becomes ERR_UNKNOWN_ERROR and
# the whole save fails, instead of this key falling back to its default.
except (TypeError, ValueError, OverflowError):
new_options.pop(CONF_ANTI_CREASE_FINALIZE_RATIO, None)
# #430: seconds, 0 = off. Clamped to [0, CURVE_PREROLL_MAX_SECONDS] so a
# mistyped value cannot drag minutes of unrelated standby into a curve; empty
# or non-numeric drops the key and restores the default (off).
if CONF_CURVE_PREROLL_SECONDS in new_options:
_raw_pr = new_options[CONF_CURVE_PREROLL_SECONDS]
if _raw_pr in (None, ""):
new_options.pop(CONF_CURVE_PREROLL_SECONDS, None)
else:
try:
_pr = float(_raw_pr)
if not math.isfinite(_pr):
raise ValueError("non-finite")
new_options[CONF_CURVE_PREROLL_SECONDS] = min(
CURVE_PREROLL_MAX_SECONDS, max(0.0, _pr)
)
except (TypeError, ValueError, OverflowError):
new_options.pop(CONF_CURVE_PREROLL_SECONDS, None)
# A None outside the clearable selectors means "not set", not a value: the
# per-setting Revert sends the changelog's `old`, which is null for a setting
# never saved before. Stored, it would survive options.get(key, DEFAULT) and
# break the float()/int() casts at setup, so drop the key and let the default
# apply again; this also cleans nulls persisted by earlier builds.
_pre_strip_keys = set(new_options)
new_options = strip_null_options(new_options)
dropped_null_keys = _pre_strip_keys - set(new_options)
# Partition identity out of options: the display name is carried by the
# entry title, never persisted in options (matches the config-flow invariant
# that CONF_NAME is absent from options).
for key in _OPTIONS_IDENTITY_KEYS:
new_options.pop(key, None)
update_kwargs: dict[str, Any] = {"options": new_options}
if isinstance(submitted_name, str) and submitted_name.strip():
update_kwargs["title"] = submitted_name.strip()
# Settings change history (D7): diff the pre-update effective options against
# the post-normalization values, but only for keys the user actually
# submitted, and persist BEFORE async_update_entry (which schedules a reload
# that rebuilds the store). A changelog failure must never block the save.
# Serialised per entry (#442 follow-up): the snapshot below, the changelog
# write and `async_update_entry` must be one critical section. Without it
# two concurrent writers both read the same `entry.options`, both record the
# same "old" value in the settings history, and the second update silently
# overwrites the first - so a later per-setting revert restores a value that
# was never current. The import handlers already hold this lock; the option
# writers did not. acquire/release rather than `async with` so the handler
# keeps its early-return validation paths. The OPTIONS lock, not the write
# lock: see `_entry_options_lock` for why a save must not queue behind a
# reprocess or an ML training run.
lock = _entry_options_lock(hass, msg["entry_id"])
await lock.acquire()
try:
old_effective = {**entry.data, **entry.options}
# Keys dropped by the null-strip above are recorded as a change to None
# ("reverted to unset") so the history still shows what happened; a
# None -> None no-op is skipped by _diff_option_changes.
# Build the new options from the *existing* options plus the submitted
# values only. Never spread entry.data in: that would copy identity and
# data-only keys (name, initial_profile, stale creation-time identity) into
# options where they don't belong. Tunables (including device_type /
# power_sensor / min_power, which live in options post-3.6) are preserved
# from entry.options and overridden by the submission.
new_options = {**entry.options, **msg["options"]}
# Capture the submitted display name for the entry title before it is
# stripped out of options below.
submitted_name = new_options.get(CONF_NAME)
# Mirror the OptionsFlow save-time normalization so the panel can never
# persist stale or invalid values:
# - a cleared selector (entity / linked device / trigger) becomes None so
# the link or subscription is removed rather than left dangling;
# - pump-only keys are dropped for non-pump device types;
# - the transient "apply suggestions" flag is never stored.
for key in (
CONF_EXTERNAL_END_TRIGGER,
CONF_DOOR_SENSOR_ENTITY,
CONF_UNLOAD_CONFIRM_ENTITY,
CONF_LINKED_DEVICE,
CONF_SWITCH_ENTITY,
CONF_ENERGY_SENSOR,
):
if key in new_options and not new_options[key]:
new_options[key] = None
# Resolve the effective device type option-first (submission -> existing
# options -> data -> default) so the pump-only key is dropped correctly even
# when the submission omits device_type.
effective_device_type = new_options.get(
CONF_DEVICE_TYPE, entry.data.get(CONF_DEVICE_TYPE, DEFAULT_DEVICE_TYPE)
)
if effective_device_type != DEVICE_TYPE_PUMP:
new_options.pop(CONF_PUMP_STUCK_DURATION, None)
# Numeric-finite validation for fields that the cycle-detector float()-casts at
# build time; coerce bad submissions to the compiled default so storage stays clean.
if CONF_DISHWASHER_END_SPIKE_QUIET_RELEASE in new_options:
try:
_qr = float(new_options[CONF_DISHWASHER_END_SPIKE_QUIET_RELEASE])
if not math.isfinite(_qr):
raise ValueError("non-finite")
new_options[CONF_DISHWASHER_END_SPIKE_QUIET_RELEASE] = _qr
# OverflowError too, same reason as the anti-crease block below
# (register item 194): an unbounded int from JSON raises on float()
# rather than returning inf, and uncaught it fails the WHOLE save
# with unknown_error instead of dropping this one key.
except (TypeError, ValueError, OverflowError):
new_options[CONF_DISHWASHER_END_SPIKE_QUIET_RELEASE] = (
DISHWASHER_END_SPIKE_QUIET_RELEASE_SECONDS
)
# Smart-Termination duration ratio (#393): fraction of expected duration, so it
# is meaningless outside [0.50, 1.00] - clamp valid submissions to the range.
# An empty or non-numeric value drops the key so the device-type default
# (resolved in the config builder, 0.99 dishwasher / 0.98 other) applies again;
# coercing to a single scalar default here would be wrong for dishwashers.
if CONF_SMART_TERMINATION_DURATION_RATIO in new_options:
_raw_str = new_options[CONF_SMART_TERMINATION_DURATION_RATIO]
if _raw_str in (None, ""):
new_options.pop(CONF_SMART_TERMINATION_DURATION_RATIO, None)
else:
try:
_str = float(_raw_str)
if not math.isfinite(_str):
raise ValueError("non-finite")
new_options[CONF_SMART_TERMINATION_DURATION_RATIO] = min(
1.0, max(0.5, _str)
)
# OverflowError too, see the anti-crease block below.
except (TypeError, ValueError, OverflowError):
new_options.pop(CONF_SMART_TERMINATION_DURATION_RATIO, None)
# #429: the anti-crease finalise ratio is the same kind of value against the
# same kind of mean, and equally meaningless outside [0.50, 1.00]. An empty or
# non-numeric submission drops the key so DEFAULT_ANTI_CREASE_FINALIZE_RATIO
# applies again.
if CONF_ANTI_CREASE_FINALIZE_RATIO in new_options:
_raw_ac = new_options[CONF_ANTI_CREASE_FINALIZE_RATIO]
if _raw_ac in (None, ""):
new_options.pop(CONF_ANTI_CREASE_FINALIZE_RATIO, None)
else:
try:
_ac = float(_raw_ac)
if not math.isfinite(_ac):
raise ValueError("non-finite")
new_options[CONF_ANTI_CREASE_FINALIZE_RATIO] = min(
ANTI_CREASE_FINALIZE_RATIO_MAX,
max(ANTI_CREASE_FINALIZE_RATIO_MIN, _ac),
)
# OverflowError too (register item 194): json parses an integer literal
# of any length into an unbounded int, and float() on one of those raises
# rather than returning inf. Uncaught it becomes ERR_UNKNOWN_ERROR and
# the whole save fails, instead of this key falling back to its default.
except (TypeError, ValueError, OverflowError):
new_options.pop(CONF_ANTI_CREASE_FINALIZE_RATIO, None)
# #430: seconds, 0 = off. Clamped to [0, CURVE_PREROLL_MAX_SECONDS] so a
# mistyped value cannot drag minutes of unrelated standby into a curve; empty
# or non-numeric drops the key and restores the default (off).
if CONF_CURVE_PREROLL_SECONDS in new_options:
_raw_pr = new_options[CONF_CURVE_PREROLL_SECONDS]
if _raw_pr in (None, ""):
new_options.pop(CONF_CURVE_PREROLL_SECONDS, None)
else:
try:
_pr = float(_raw_pr)
if not math.isfinite(_pr):
raise ValueError("non-finite")
new_options[CONF_CURVE_PREROLL_SECONDS] = min(
CURVE_PREROLL_MAX_SECONDS, max(0.0, _pr)
)
except (TypeError, ValueError, OverflowError):
new_options.pop(CONF_CURVE_PREROLL_SECONDS, None)
# A None outside the clearable selectors means "not set", not a value: the
# per-setting Revert sends the changelog's `old`, which is null for a setting
# never saved before. Stored, it would survive options.get(key, DEFAULT) and
# break the float()/int() casts at setup, so drop the key and let the default
# apply again; this also cleans nulls persisted by earlier builds.
_pre_strip_keys = set(new_options)
new_options = strip_null_options(new_options)
dropped_null_keys = _pre_strip_keys - set(new_options)
# Partition identity out of options: the display name is carried by the
# entry title, never persisted in options (matches the config-flow invariant
# that CONF_NAME is absent from options).
for key in _OPTIONS_IDENTITY_KEYS:
new_options.pop(key, None)
update_kwargs: dict[str, Any] = {"options": new_options}
if isinstance(submitted_name, str) and submitted_name.strip():
update_kwargs["title"] = submitted_name.strip()
# Settings change history (D7): diff the pre-update effective options against
# the post-normalization values, but only for keys the user actually
# submitted, and persist BEFORE async_update_entry (which schedules a reload
# that rebuilds the store). A changelog failure must never block the save.
# Through the shared helper, not a second copy of it. The per-setting
# Revert trusts this history, so the two writers have to follow one
# contract - and `_record_option_changes`'s own docstring already says it
# replaces the inline version that used to live here. Keys dropped by the
# null-strip above are still recorded as a change to None ("reverted to
# unset"); a None -> None no-op is skipped inside `_diff_option_changes`.
submitted_post = {
k: new_options.get(k)
for k in msg["options"]
if k in new_options or k in dropped_null_keys
}
changes = _diff_option_changes(old_effective, submitted_post)
if changes:
manager = _get_manager(hass, msg["entry_id"])
store = getattr(manager, "profile_store", None) if manager else None
if store is not None:
await store.async_record_settings_changes(changes)
except Exception as exc: # pylint: disable=broad-exception-caught
_LOGGER.debug(
"Settings changelog recording failed for %s: %s", msg["entry_id"], exc
)
await _record_option_changes(hass, entry, submitted_post, "set_options")
# When online features are disabled, clear the persisted store account so the
# user's identity isn't silently retained after they opt out.
from .const import CONF_ENABLE_ONLINE_FEATURES # pylint: disable=import-outside-toplevel
was_online = bool(entry.options.get(CONF_ENABLE_ONLINE_FEATURES, False))
now_online = bool(new_options.get(CONF_ENABLE_ONLINE_FEATURES, False))
if was_online and not now_online:
try:
manager = _get_manager(hass, msg["entry_id"])
store = getattr(manager, "profile_store", None) if manager else None
if store is not None:
await store.clear_store_account()
await store.async_save()
except Exception: # pylint: disable=broad-exception-caught
pass
# When online features are disabled, clear the persisted store account so the
# user's identity isn't silently retained after they opt out.
from .const import CONF_ENABLE_ONLINE_FEATURES # pylint: disable=import-outside-toplevel
was_online = bool(entry.options.get(CONF_ENABLE_ONLINE_FEATURES, False))
now_online = bool(new_options.get(CONF_ENABLE_ONLINE_FEATURES, False))
if was_online and not now_online:
try:
manager = _get_manager(hass, msg["entry_id"])
store = getattr(manager, "profile_store", None) if manager else None
if store is not None:
await store.clear_store_account()
await store.async_save()
except Exception: # pylint: disable=broad-exception-caught
# Warning, not a silent pass: the user asked for this identity to
# be removed, and a bare `pass` here leaves it in storage with no
# trace. A bare `pass` is also what hid the #445 standby advisory
# being dead for a whole review round (register item 325).
_LOGGER.warning(
"Could not clear the store account for %s after online "
"features were disabled; the stored identity may remain",
msg["entry_id"],
exc_info=True,
)
hass.config_entries.async_update_entry(entry, **update_kwargs)
_send_result(connection, msg["id"], "set_options", {"success": True})
hass.config_entries.async_update_entry(entry, **update_kwargs)
_send_result(connection, msg["id"], "set_options", {"success": True})
finally:
lock.release()
@websocket_api.websocket_command(
@@ -2704,7 +2895,10 @@ def ws_get_phase_catalog(
{
vol.Required("type"): "ha_washdata/create_phase",
vol.Required("entry_id"): str,
vol.Required("device_type"): str,
# Optional since #450: an omitted or empty device_type resolves to
# ``manager.device_type``, the exact value ``ws_get_phase_catalog``
# lists against, so create and list can never disagree about scope.
vol.Optional("device_type", default=""): str,
vol.Required("name"): str,
vol.Optional("description", default=""): str,
}
@@ -2722,9 +2916,34 @@ async def ws_create_phase(
_err_not_found(connection, msg["id"], entry_id)
return
# #450: a panel served by an entry whose device_type lives only in
# ``entry.data`` used to send the ``'washing_machine'`` fallback here, so the
# phase was stored under a scope the catalog never lists - invisible, yet
# still tripping the duplicate check on the next attempt.
device_type = str(msg.get("device_type") or "").strip() or str(
getattr(manager, "device_type", "") or ""
).strip()
# ...and refuse rather than store under an empty scope. `manager.device_type`
# reads `options.get(CONF_DEVICE_TYPE, data.get(..., DEFAULT))`, and `.get`
# hands back a persisted empty string or null verbatim instead of the default
# - the #389 class that `strip_null_options` exists for. An empty scope is the
# worst outcome available here, not a harmless one: `list_phase_catalog` never
# lists it, so the phase is invisible, yet it still trips the duplicate check
# on the next attempt, so the user cannot create it again either. That is
# exactly the #450 symptom the comment above describes, reached from the
# entry's own options rather than from the panel's payload.
if not device_type:
connection.send_error(
msg["id"],
"invalid_device_type",
"This entry has no usable device type, so a phase created now could "
"not be listed again. Re-save the device settings and retry.",
)
return
try:
await manager.profile_store.async_create_custom_phase(
msg["device_type"], msg["name"], msg.get("description", "")
device_type, msg["name"], msg.get("description", "")
)
_send_result(connection, msg["id"], "create_phase", {"success": True})
except ValueError as exc:
@@ -3484,9 +3703,12 @@ async def ws_import_config(
if entry and config_updates:
entry_options_updates = dict(config_updates.get("entry_options", {}))
# Identity must never be persisted into options; the display name
# rides the entry title. device_type/power_sensor/min_power stay
# in options and are applied as tunables.
for key in _OPTIONS_IDENTITY_KEYS:
# rides the entry title. Local entity/device bindings are dropped
# too: they are tunables when the panel writes them, but an
# import carries the exporter's ids and would repoint this device
# at entities that do not exist here (item 317). device_type and
# min_power are genuinely portable and stay.
for key in _IMPORT_LOCAL_BINDING_KEYS:
entry_options_updates.pop(key, None)
if entry_options_updates:
# Apply the imported tunables on top of the current options;
@@ -3495,16 +3717,37 @@ async def ws_import_config(
# one), and a persisted null survives options.get(key, DEFAULT)
# and breaks setup (#389), so the same write-boundary strip as
# ws_set_options applies here.
new_options = strip_null_options(
{**entry.options, **entry_options_updates}
)
hass.config_entries.async_update_entry(entry, options=new_options)
# Nested inside the write lock this handler already holds;
# order is always write -> options, so no deadlock.
async with _entry_options_lock(hass, entry_id):
# Read INSIDE the lock. Built before it, `new_options`
# is a snapshot of `entry.options` from before the
# `async with` suspended - so a `ws_set_options` that
# committed while we waited would be silently reverted
# by this write, and the changelog would record the
# post-save value as `old`. The other four option
# writers already read inside their lock; this was the
# one that did not.
new_options = strip_null_options(
{**entry.options, **entry_options_updates}
)
await _record_option_changes(
hass, entry, entry_options_updates, "import_config"
)
hass.config_entries.async_update_entry(
entry, options=new_options
)
# NB: config_updates["entry_data"] is intentionally NOT written to
# entry.data. export_data ships the raw, un-redacted entry.data of
# the *source* device (its power_sensor and other identity), so
# blindly applying it would hijack this device's sensor binding.
# Identity changes must go through the reconfigure flow.
# An old payload re-arms the one-time banked-tail repair, and the
# options write above is the only thing here that could reload the
# entry - so an import that carries no options would otherwise leave
# the imported tails inflating avg_duration until the next restart.
manager.async_schedule_banked_tail_repair()
manager.notify_update()
_send_result(connection, msg["id"], "import_config", {"success": True})
except json.JSONDecodeError as exc:
@@ -3725,12 +3968,23 @@ async def ws_import_config_selective(
for key in _OPTIONS_IDENTITY_KEYS:
filtered.pop(key, None)
if filtered:
hass.config_entries.async_update_entry(
entry, options={**entry.options, **filtered}
)
# Nested inside the write lock this handler already holds;
# order is always write -> options, so no deadlock.
async with _entry_options_lock(hass, entry_id):
await _record_option_changes(
hass, entry, filtered, "store_device_package"
)
hass.config_entries.async_update_entry(
entry, options={**entry.options, **filtered}
)
settings_applied = len(filtered)
summary = {**summary, "settings_applied": settings_applied}
# Same as the wholesale path: `apply_settings=False` (and any import
# whose settings subset comes back empty) skips the options write
# above, so nothing reloads the entry and the re-armed repair would
# wait for a restart.
manager.async_schedule_banked_tail_repair()
manager.notify_update()
_send_result(
connection, msg["id"], "import_config_selective",
@@ -3900,11 +4154,17 @@ async def ws_apply_suggestions(
# reload that rebuilds the store, so persist the cleared state first.
cycle_count = len(manager.profile_store.get_past_cycles())
manager.profile_store.set_suggestion_apply_cycle_count(cycle_count)
await manager.profile_store.clear_suggestions()
# Suggested values are all tunables -> layer them onto the existing
# options; never spread entry.data into options.
new_options = {**entry.options, **updates}
hass.config_entries.async_update_entry(entry, options=new_options)
# Same critical section as ws_set_options (#442 follow-up): the
# changelog snapshot and the options write must be atomic per entry.
async with _entry_options_lock(hass, entry_id):
# Record BEFORE clear_suggestions/async_update_entry: both persist,
# and the reload the latter schedules rebuilds the store (#442).
await _record_option_changes(hass, entry, updates, "apply_suggestions")
await manager.profile_store.clear_suggestions()
# Suggested values are all tunables -> layer them onto the existing
# options; never spread entry.data into options.
new_options = {**entry.options, **updates}
hass.config_entries.async_update_entry(entry, options=new_options)
manager.notify_update()
_send_result(connection, msg["id"], "apply_suggestions", {"success": True, "applied": list(updates.keys())}
@@ -4055,6 +4315,20 @@ async def ws_get_cycle_power_data(
_LOGGER.debug("Error getting cycle power data %s: %s", cycle_id, exc)
_ds = _downsample(samples)
# The matched profile's expected curve, projected onto THIS cycle's time axis
# through the same alignment the artifact/conformance comparison used, so the
# overlay, the trace and the artifact shading all share one axis. Computed on
# the full trace but emitted at the thinned x values the panel plots.
if meta.get("profile_name") and len(_ds) >= 4 and len(samples) >= 4:
try:
meta["expected"] = await hass.async_add_executor_job(
manager.profile_store.expected_curve_for_cycle,
meta["profile_name"],
samples,
[float(p[0]) for p in _ds],
)
except Exception as exc: # pylint: disable=broad-exception-caught
_LOGGER.debug("Expected curve for cycle %s failed: %s", cycle_id, exc)
_send_result(connection, msg["id"], "get_cycle_power_data", {
"cycle_id": cycle_id,
"samples": _ds,
+9 -1
View File
@@ -92,6 +92,10 @@ class DeviceInfo(TypedDict):
# device type (#396/#393), so the device-list conflict/suggestion badges can score
# an unset field against the value the integration would use (matches the Settings tab).
option_defaults: dict[str, Any]
# Set by ws_get_devices whenever a manager is loaded: the #445 cause-1
# advisory, or None when there is no pattern (or the probe failed). Declared
# because the panel reads it; the handler always assigns one or the other.
standby_above_stop: dict[str, Any] | None
class GetDevicesResponse(TypedDict):
@@ -304,6 +308,10 @@ class GetCyclePowerDataResponse(TypedDict, total=False):
labelable: bool
editable: bool
cycle_origin: str
# Spread from `**meta` when the cycle has a matched profile and
# `expected_curve_for_cycle` succeeds, so it is optional rather than absent:
# the panel draws the expected curve from it.
expected: list[list[float]] | None
class AnalyzeSplitResponse(TypedDict):
@@ -952,7 +960,7 @@ WS_COMMANDS: dict[str, dict] = {
"get_phase_catalog": {"params": [_entry(), _p("device_type", "str|null", False)]},
"create_phase": {"params": [
_entry(),
_p("device_type", "str"),
_p("device_type", "str", False),
_p("name", "str"),
_p("description", "str", False),
]},
@@ -3,17 +3,17 @@
"assets": {
"ha-washdata-panel.js": {
"artifact": "ha-washdata-panel.min.js",
"source_sha256": "56d8a3b1f6637b622d6300df1320d962b7399a93bf66b42ffb73bc32baf14b47",
"artifact_sha256": "06916396cf1974fd4266d24e6f0678375031d220634d377b6cda39d0eb6d3a34",
"source_bytes": 917813,
"artifact_bytes": 597157
"source_sha256": "4d89abc002319d37b42a2644375516a515ffe0607e663326d4ad22f72efd8660",
"artifact_sha256": "6034a4a223c7192064736c11d184317c9885dad819bf74ca0306358673844e30",
"source_bytes": 948888,
"artifact_bytes": 607586
},
"ha-washdata-card.js": {
"artifact": "ha-washdata-card.min.js",
"source_sha256": "a4c8d484bf7fdbdf5d5eb82d8f31fd7d3c73bc22f2ab16e5f2c374db528baa8e",
"artifact_sha256": "0c344e93071a232b1622a62272547556a301c78e1cb25b94b9c755bb9bd34fd8",
"source_bytes": 55871,
"artifact_bytes": 30307
"source_sha256": "8782b8429957cfdd344efa36ed5fe4334818dbf199b48245f14eba02f1243b0b",
"artifact_sha256": "d6a42777e7102610983ff27638e9b4640cfaa7c797c668532e31c1afaf1a5d70",
"source_bytes": 56140,
"artifact_bytes": 30343
}
}
}

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